diff --git a/sys/dev/ath/ath_hal/ah_internal.h b/sys/dev/ath/ath_hal/ah_internal.h index 8f7ba65cb0c5..d284dfdf8605 100644 --- a/sys/dev/ath/ath_hal/ah_internal.h +++ b/sys/dev/ath/ath_hal/ah_internal.h @@ -1,804 +1,806 @@ /* * 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$ */ #ifndef _ATH_AH_INTERAL_H_ #define _ATH_AH_INTERAL_H_ /* * Atheros Device Hardware Access Layer (HAL). * * Internal definitions. */ #define AH_NULL 0 #define AH_MIN(a,b) ((a)<(b)?(a):(b)) #define AH_MAX(a,b) ((a)>(b)?(a):(b)) #include #ifndef NBBY #define NBBY 8 /* number of bits/byte */ #endif #ifndef roundup #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ #endif #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif #ifndef offsetof #define offsetof(type, field) ((size_t)(&((type *)0)->field)) #endif typedef struct { uint16_t start; /* first register */ uint16_t end; /* ending register or zero */ } HAL_REGRANGE; typedef struct { uint32_t addr; /* regiser address/offset */ uint32_t value; /* value to write */ } HAL_REGWRITE; /* * Transmit power scale factor. * * NB: This is not public because we want to discourage the use of * scaling; folks should use the tx power limit interface. */ typedef enum { HAL_TP_SCALE_MAX = 0, /* no scaling (default) */ HAL_TP_SCALE_50 = 1, /* 50% of max (-3 dBm) */ HAL_TP_SCALE_25 = 2, /* 25% of max (-6 dBm) */ HAL_TP_SCALE_12 = 3, /* 12% of max (-9 dBm) */ HAL_TP_SCALE_MIN = 4, /* min, but still on */ } HAL_TP_SCALE; typedef enum { HAL_CAP_RADAR = 0, /* Radar capability */ HAL_CAP_AR = 1, /* AR capability */ } HAL_PHYDIAG_CAPS; /* * Each chip or class of chips registers to offer support. */ struct ath_hal_chip { const char *name; const char *(*probe)(uint16_t vendorid, uint16_t devid); struct ath_hal *(*attach)(uint16_t devid, HAL_SOFTC, HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS *error); }; #ifndef AH_CHIP #define AH_CHIP(_name, _probe, _attach) \ static struct ath_hal_chip _name##_chip = { \ .name = #_name, \ .probe = _probe, \ .attach = _attach \ }; \ OS_DATA_SET(ah_chips, _name##_chip) #endif /* * Each RF backend registers to offer support; this is mostly * used by multi-chip 5212 solutions. Single-chip solutions * have a fixed idea about which RF to use. */ struct ath_hal_rf { const char *name; HAL_BOOL (*probe)(struct ath_hal *ah); HAL_BOOL (*attach)(struct ath_hal *ah, HAL_STATUS *ecode); }; #ifndef AH_RF #define AH_RF(_name, _probe, _attach) \ static struct ath_hal_rf _name##_rf = { \ .name = __STRING(_name), \ .probe = _probe, \ .attach = _attach \ }; \ OS_DATA_SET(ah_rfs, _name##_rf) #endif struct ath_hal_rf *ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode); /* * Maximum number of internal channels. Entries are per unique * frequency so this might be need to be increased to handle all * usage cases; typically no more than 32 are really needed but * dynamically allocating the data structures is a bit painful * right now. */ #ifndef AH_MAXCHAN #define AH_MAXCHAN 96 #endif /* * Internal per-channel state. These are found * using ic_devdata in the ieee80211_channel. */ typedef struct { uint16_t channel; /* h/w frequency, NB: may be mapped */ uint8_t privFlags; #define CHANNEL_IQVALID 0x01 /* IQ calibration valid */ #define CHANNEL_ANI_INIT 0x02 /* ANI state initialized */ #define CHANNEL_ANI_SETUP 0x04 /* ANI state setup */ uint8_t calValid; /* bitmask of cal types */ int8_t iCoff; int8_t qCoff; int16_t rawNoiseFloor; int16_t noiseFloorAdjust; uint16_t mainSpur; /* cached spur value for this channel */ } HAL_CHANNEL_INTERNAL; /* channel requires noise floor check */ #define CHANNEL_NFCREQUIRED IEEE80211_CHAN_PRIV0 /* all full-width channels */ #define IEEE80211_CHAN_ALLFULL \ (IEEE80211_CHAN_ALL - (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) #define IEEE80211_CHAN_ALLTURBOFULL \ (IEEE80211_CHAN_ALLTURBO - \ (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) typedef struct { uint32_t halChanSpreadSupport : 1, halSleepAfterBeaconBroken : 1, halCompressSupport : 1, halBurstSupport : 1, halFastFramesSupport : 1, halChapTuningSupport : 1, halTurboGSupport : 1, halTurboPrimeSupport : 1, halMicAesCcmSupport : 1, halMicCkipSupport : 1, halMicTkipSupport : 1, halTkipMicTxRxKeySupport : 1, halCipherAesCcmSupport : 1, halCipherCkipSupport : 1, halCipherTkipSupport : 1, halPSPollBroken : 1, halVEOLSupport : 1, halBssIdMaskSupport : 1, halMcastKeySrchSupport : 1, halTsfAddSupport : 1, halChanHalfRate : 1, halChanQuarterRate : 1, halHTSupport : 1, halRfSilentSupport : 1, halHwPhyCounterSupport : 1, halWowSupport : 1, halWowMatchPatternExact : 1, halAutoSleepSupport : 1, halFastCCSupport : 1, halBtCoexSupport : 1; uint32_t halRxStbcSupport : 1, halTxStbcSupport : 1, halGTTSupport : 1, halCSTSupport : 1, halRifsRxSupport : 1, halRifsTxSupport : 1, halExtChanDfsSupport : 1, halForcePpmSupport : 1, halEnhancedPmSupport : 1, halMbssidAggrSupport : 1, halBssidMatchSupport : 1; uint32_t halWirelessModes; uint16_t halTotalQueues; uint16_t halKeyCacheSize; uint16_t halLow5GhzChan, halHigh5GhzChan; uint16_t halLow2GhzChan, halHigh2GhzChan; int halTstampPrecision; int halRtsAggrLimit; uint8_t halTxChainMask; uint8_t halRxChainMask; uint8_t halNumGpioPins; uint8_t halNumAntCfg2GHz; uint8_t halNumAntCfg5GHz; uint32_t halIntrMask; } HAL_CAPABILITIES; struct regDomain; /* * The ``private area'' follows immediately after the ``public area'' * in the data structure returned by ath_hal_attach. Private data are * used by device-independent code such as the regulatory domain support. * In general, code within the HAL should never depend on data in the * public area. Instead any public data needed internally should be * shadowed here. * * When declaring a device-specific ath_hal data structure this structure * is assumed to at the front; e.g. * * struct ath_hal_5212 { * struct ath_hal_private ah_priv; * ... * }; * * It might be better to manage the method pointers in this structure * using an indirect pointer to a read-only data structure but this would * disallow class-style method overriding. */ struct ath_hal_private { struct ath_hal h; /* public area */ /* NB: all methods go first to simplify initialization */ HAL_BOOL (*ah_getChannelEdges)(struct ath_hal*, uint16_t channelFlags, uint16_t *lowChannel, uint16_t *highChannel); u_int (*ah_getWirelessModes)(struct ath_hal*); HAL_BOOL (*ah_eepromRead)(struct ath_hal *, u_int off, uint16_t *data); HAL_BOOL (*ah_eepromWrite)(struct ath_hal *, u_int off, uint16_t data); HAL_BOOL (*ah_getChipPowerLimits)(struct ath_hal *, struct ieee80211_channel *); int16_t (*ah_getNfAdjust)(struct ath_hal *, const HAL_CHANNEL_INTERNAL*); void (*ah_getNoiseFloor)(struct ath_hal *, int16_t nfarray[]); void *ah_eeprom; /* opaque EEPROM state */ uint16_t ah_eeversion; /* EEPROM version */ void (*ah_eepromDetach)(struct ath_hal *); HAL_STATUS (*ah_eepromGet)(struct ath_hal *, int, void *); HAL_BOOL (*ah_eepromSet)(struct ath_hal *, int, int); uint16_t (*ah_getSpurChan)(struct ath_hal *, int, HAL_BOOL); HAL_BOOL (*ah_eepromDiag)(struct ath_hal *, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); /* * Device revision information. */ uint16_t ah_devid; /* PCI device ID */ uint16_t ah_subvendorid; /* PCI subvendor ID */ uint32_t ah_macVersion; /* MAC version id */ uint16_t ah_macRev; /* MAC revision */ uint16_t ah_phyRev; /* PHY revision */ uint16_t ah_analog5GhzRev; /* 2GHz radio revision */ uint16_t ah_analog2GhzRev; /* 5GHz radio revision */ uint8_t ah_ispcie; /* PCIE, special treatment */ HAL_OPMODE ah_opmode; /* operating mode from reset */ const struct ieee80211_channel *ah_curchan;/* operating channel */ HAL_CAPABILITIES ah_caps; /* device capabilities */ uint32_t ah_diagreg; /* user-specified AR_DIAG_SW */ int16_t ah_powerLimit; /* tx power cap */ uint16_t ah_maxPowerLevel; /* calculated max tx power */ u_int ah_tpScale; /* tx power scale factor */ uint32_t ah_11nCompat; /* 11n compat controls */ + uint8_t ah_txtrig_level; /* current Tx trigger level */ + uint8_t ah_max_txtrig_level; /* max tx trigger level */ /* * State for regulatory domain handling. */ HAL_REG_DOMAIN ah_currentRD; /* EEPROM regulatory domain */ HAL_CHANNEL_INTERNAL ah_channels[AH_MAXCHAN]; /* private chan state */ u_int ah_nchan; /* valid items in ah_channels */ const struct regDomain *ah_rd2GHz; /* reg state for 2G band */ const struct regDomain *ah_rd5GHz; /* reg state for 5G band */ uint8_t ah_coverageClass; /* coverage class */ /* * RF Silent handling; setup according to the EEPROM. */ uint16_t ah_rfsilent; /* GPIO pin + polarity */ HAL_BOOL ah_rfkillEnabled; /* enable/disable RfKill */ /* * Diagnostic support for discriminating HIUERR reports. */ uint32_t ah_fatalState[6]; /* AR_ISR+shadow regs */ int ah_rxornIsFatal; /* how to treat HAL_INT_RXORN */ }; #define AH_PRIVATE(_ah) ((struct ath_hal_private *)(_ah)) #define ath_hal_getChannelEdges(_ah, _cf, _lc, _hc) \ AH_PRIVATE(_ah)->ah_getChannelEdges(_ah, _cf, _lc, _hc) #define ath_hal_getWirelessModes(_ah) \ AH_PRIVATE(_ah)->ah_getWirelessModes(_ah) #define ath_hal_eepromRead(_ah, _off, _data) \ AH_PRIVATE(_ah)->ah_eepromRead(_ah, _off, _data) #define ath_hal_eepromWrite(_ah, _off, _data) \ AH_PRIVATE(_ah)->ah_eepromWrite(_ah, _off, _data) #define ath_hal_gpioCfgOutput(_ah, _gpio, _type) \ (_ah)->ah_gpioCfgOutput(_ah, _gpio, _type) #define ath_hal_gpioCfgInput(_ah, _gpio) \ (_ah)->ah_gpioCfgInput(_ah, _gpio) #define ath_hal_gpioGet(_ah, _gpio) \ (_ah)->ah_gpioGet(_ah, _gpio) #define ath_hal_gpioSet(_ah, _gpio, _val) \ (_ah)->ah_gpioSet(_ah, _gpio, _val) #define ath_hal_gpioSetIntr(_ah, _gpio, _ilevel) \ (_ah)->ah_gpioSetIntr(_ah, _gpio, _ilevel) #define ath_hal_getpowerlimits(_ah, _chan) \ AH_PRIVATE(_ah)->ah_getChipPowerLimits(_ah, _chan) #define ath_hal_getNfAdjust(_ah, _c) \ AH_PRIVATE(_ah)->ah_getNfAdjust(_ah, _c) #define ath_hal_getNoiseFloor(_ah, _nfArray) \ AH_PRIVATE(_ah)->ah_getNoiseFloor(_ah, _nfArray) #define ath_hal_configPCIE(_ah, _reset) \ (_ah)->ah_configPCIE(_ah, _reset) #define ath_hal_disablePCIE(_ah) \ (_ah)->ah_disablePCIE(_ah) #define ath_hal_eepromDetach(_ah) do { \ if (AH_PRIVATE(_ah)->ah_eepromDetach != AH_NULL) \ AH_PRIVATE(_ah)->ah_eepromDetach(_ah); \ } while (0) #define ath_hal_eepromGet(_ah, _param, _val) \ AH_PRIVATE(_ah)->ah_eepromGet(_ah, _param, _val) #define ath_hal_eepromSet(_ah, _param, _val) \ AH_PRIVATE(_ah)->ah_eepromSet(_ah, _param, _val) #define ath_hal_eepromGetFlag(_ah, _param) \ (AH_PRIVATE(_ah)->ah_eepromGet(_ah, _param, AH_NULL) == HAL_OK) #define ath_hal_getSpurChan(_ah, _ix, _is2G) \ AH_PRIVATE(_ah)->ah_getSpurChan(_ah, _ix, _is2G) #define ath_hal_eepromDiag(_ah, _request, _a, _asize, _r, _rsize) \ AH_PRIVATE(_ah)->ah_eepromDiag(_ah, _request, _a, _asize, _r, _rsize) #ifndef _NET_IF_IEEE80211_H_ /* * Stuff that would naturally come from _ieee80211.h */ #define IEEE80211_ADDR_LEN 6 #define IEEE80211_WEP_IVLEN 3 /* 24bit */ #define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ #define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ #define IEEE80211_CRC_LEN 4 #define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \ (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN)) #endif /* _NET_IF_IEEE80211_H_ */ #define HAL_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001 #define INIT_AIFS 2 #define INIT_CWMIN 15 #define INIT_CWMIN_11B 31 #define INIT_CWMAX 1023 #define INIT_SH_RETRY 10 #define INIT_LG_RETRY 10 #define INIT_SSH_RETRY 32 #define INIT_SLG_RETRY 32 typedef struct { uint32_t tqi_ver; /* HAL TXQ verson */ HAL_TX_QUEUE tqi_type; /* hw queue type*/ HAL_TX_QUEUE_SUBTYPE tqi_subtype; /* queue subtype, if applicable */ HAL_TX_QUEUE_FLAGS tqi_qflags; /* queue flags */ uint32_t tqi_priority; uint32_t tqi_aifs; /* aifs */ uint32_t tqi_cwmin; /* cwMin */ uint32_t tqi_cwmax; /* cwMax */ uint16_t tqi_shretry; /* frame short retry limit */ uint16_t tqi_lgretry; /* frame long retry limit */ uint32_t tqi_cbrPeriod; uint32_t tqi_cbrOverflowLimit; uint32_t tqi_burstTime; uint32_t tqi_readyTime; uint32_t tqi_physCompBuf; uint32_t tqi_intFlags; /* flags for internal use */ } HAL_TX_QUEUE_INFO; extern HAL_BOOL ath_hal_setTxQProps(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi, const HAL_TXQ_INFO *qInfo); extern HAL_BOOL ath_hal_getTxQProps(struct ath_hal *ah, HAL_TXQ_INFO *qInfo, const HAL_TX_QUEUE_INFO *qi); typedef enum { HAL_ANI_PRESENT, /* is ANI support present */ HAL_ANI_NOISE_IMMUNITY_LEVEL, /* set level */ HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, /* enable/disable */ HAL_ANI_CCK_WEAK_SIGNAL_THR, /* enable/disable */ HAL_ANI_FIRSTEP_LEVEL, /* set level */ HAL_ANI_SPUR_IMMUNITY_LEVEL, /* set level */ HAL_ANI_MODE = 6, /* 0 => manual, 1 => auto (XXX do not change) */ HAL_ANI_PHYERR_RESET, /* reset phy error stats */ } HAL_ANI_CMD; #define HAL_SPUR_VAL_MASK 0x3FFF #define HAL_SPUR_CHAN_WIDTH 87 #define HAL_BIN_WIDTH_BASE_100HZ 3125 #define HAL_BIN_WIDTH_TURBO_100HZ 6250 #define HAL_MAX_BINS_ALLOWED 28 #define IS_CHAN_5GHZ(_c) ((_c)->channel > 4900) #define IS_CHAN_2GHZ(_c) (!IS_CHAN_5GHZ(_c)) #define IS_CHAN_IN_PUBLIC_SAFETY_BAND(_c) ((_c) > 4940 && (_c) < 4990) /* * Deduce if the host cpu has big- or litt-endian byte order. */ static __inline__ int isBigEndian(void) { union { int32_t i; char c[4]; } u; u.i = 1; return (u.c[0] == 0); } /* unalligned little endian access */ #define LE_READ_2(p) \ ((uint16_t) \ ((((const uint8_t *)(p))[0] ) | (((const uint8_t *)(p))[1]<< 8))) #define LE_READ_4(p) \ ((uint32_t) \ ((((const uint8_t *)(p))[0] ) | (((const uint8_t *)(p))[1]<< 8) |\ (((const uint8_t *)(p))[2]<<16) | (((const uint8_t *)(p))[3]<<24))) /* * Register manipulation macros that expect bit field defines * to follow the convention that an _S suffix is appended for * a shift count, while the field mask has no suffix. */ #define SM(_v, _f) (((_v) << _f##_S) & (_f)) #define MS(_v, _f) (((_v) & (_f)) >> _f##_S) #define OS_REG_RMW_FIELD(_a, _r, _f, _v) \ OS_REG_WRITE(_a, _r, \ (OS_REG_READ(_a, _r) &~ (_f)) | (((_v) << _f##_S) & (_f))) #define OS_REG_SET_BIT(_a, _r, _f) \ OS_REG_WRITE(_a, _r, OS_REG_READ(_a, _r) | (_f)) #define OS_REG_CLR_BIT(_a, _r, _f) \ OS_REG_WRITE(_a, _r, OS_REG_READ(_a, _r) &~ (_f)) /* system-configurable parameters */ extern int ath_hal_dma_beacon_response_time; /* in TU's */ extern int ath_hal_sw_beacon_response_time; /* in TU's */ extern int ath_hal_additional_swba_backoff; /* in TU's */ /* wait for the register contents to have the specified value */ extern HAL_BOOL ath_hal_wait(struct ath_hal *, u_int reg, uint32_t mask, uint32_t val); /* return the first n bits in val reversed */ extern uint32_t ath_hal_reverseBits(uint32_t val, uint32_t n); /* printf interfaces */ extern void ath_hal_printf(struct ath_hal *, const char*, ...) __printflike(2,3); extern void ath_hal_vprintf(struct ath_hal *, const char*, __va_list) __printflike(2, 0); extern const char* ath_hal_ether_sprintf(const uint8_t *mac); /* allocate and free memory */ extern void *ath_hal_malloc(size_t); extern void ath_hal_free(void *); /* common debugging interfaces */ #ifdef AH_DEBUG #include "ah_debug.h" extern int ath_hal_debug; extern void HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) __printflike(3,4); #else #define HALDEBUG(_ah, __m, _fmt, ...) #endif /* AH_DEBUG */ /* * Register logging definitions shared with ardecode. */ #include "ah_decode.h" /* * Common assertion interface. Note: it is a bad idea to generate * an assertion failure for any recoverable event. Instead catch * the violation and, if possible, fix it up or recover from it; either * with an error return value or a diagnostic messages. System software * does not panic unless the situation is hopeless. */ #ifdef AH_ASSERT extern void ath_hal_assert_failed(const char* filename, int lineno, const char* msg); #define HALASSERT(_x) do { \ if (!(_x)) { \ ath_hal_assert_failed(__FILE__, __LINE__, #_x); \ } \ } while (0) #else #define HALASSERT(_x) #endif /* AH_ASSERT */ /* * Regulatory domain support. */ /* * Return the max allowed antenna gain and apply any regulatory * domain specific changes. */ u_int ath_hal_getantennareduction(struct ath_hal *ah, const struct ieee80211_channel *chan, u_int twiceGain); /* * Return the test group for the specific channel based on * the current regulatory setup. */ u_int ath_hal_getctl(struct ath_hal *, const struct ieee80211_channel *); /* * Map a public channel definition to the corresponding * internal data structure. This implicitly specifies * whether or not the specified channel is ok to use * based on the current regulatory domain constraints. */ #ifndef AH_DEBUG static OS_INLINE HAL_CHANNEL_INTERNAL * ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c) { HAL_CHANNEL_INTERNAL *cc; HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan); cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata]; HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)); return cc; } #else /* NB: non-inline version that checks state */ HAL_CHANNEL_INTERNAL *ath_hal_checkchannel(struct ath_hal *, const struct ieee80211_channel *); #endif /* AH_DEBUG */ /* * Return the h/w frequency for a channel. This may be * different from ic_freq if this is a GSM device that * takes 2.4GHz frequencies and down-converts them. */ static OS_INLINE uint16_t ath_hal_gethwchannel(struct ath_hal *ah, const struct ieee80211_channel *c) { return ath_hal_checkchannel(ah, c)->channel; } /* * Convert between microseconds and core system clocks. */ extern u_int ath_hal_mac_clks(struct ath_hal *ah, u_int usecs); extern u_int ath_hal_mac_usec(struct ath_hal *ah, u_int clks); /* * Generic get/set capability support. Each chip overrides * this routine to support chip-specific capabilities. */ extern HAL_STATUS ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result); extern HAL_BOOL ath_hal_setcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t setting, HAL_STATUS *status); /* * Diagnostic interface. This is an open-ended interface that * is opaque to applications. Diagnostic programs use this to * retrieve internal data structures, etc. There is no guarantee * that calling conventions for calls other than HAL_DIAG_REVS * are stable between HAL releases; a diagnostic application must * use the HAL revision information to deal with ABI/API differences. * * NB: do not renumber these, certain codes are publicly used. */ enum { HAL_DIAG_REVS = 0, /* MAC/PHY/Radio revs */ HAL_DIAG_EEPROM = 1, /* EEPROM contents */ HAL_DIAG_EEPROM_EXP_11A = 2, /* EEPROM 5112 power exp for 11a */ HAL_DIAG_EEPROM_EXP_11B = 3, /* EEPROM 5112 power exp for 11b */ HAL_DIAG_EEPROM_EXP_11G = 4, /* EEPROM 5112 power exp for 11g */ HAL_DIAG_ANI_CURRENT = 5, /* ANI current channel state */ HAL_DIAG_ANI_OFDM = 6, /* ANI OFDM timing error stats */ HAL_DIAG_ANI_CCK = 7, /* ANI CCK timing error stats */ HAL_DIAG_ANI_STATS = 8, /* ANI statistics */ HAL_DIAG_RFGAIN = 9, /* RfGain GAIN_VALUES */ HAL_DIAG_RFGAIN_CURSTEP = 10, /* RfGain GAIN_OPTIMIZATION_STEP */ HAL_DIAG_PCDAC = 11, /* PCDAC table */ HAL_DIAG_TXRATES = 12, /* Transmit rate table */ HAL_DIAG_REGS = 13, /* Registers */ HAL_DIAG_ANI_CMD = 14, /* ANI issue command (XXX do not change!) */ HAL_DIAG_SETKEY = 15, /* Set keycache backdoor */ HAL_DIAG_RESETKEY = 16, /* Reset keycache backdoor */ HAL_DIAG_EEREAD = 17, /* Read EEPROM word */ HAL_DIAG_EEWRITE = 18, /* Write EEPROM word */ /* 19-26 removed, do not reuse */ HAL_DIAG_RDWRITE = 27, /* Write regulatory domain */ HAL_DIAG_RDREAD = 28, /* Get regulatory domain */ HAL_DIAG_FATALERR = 29, /* Read cached interrupt state */ HAL_DIAG_11NCOMPAT = 30, /* 11n compatibility tweaks */ HAL_DIAG_ANI_PARAMS = 31, /* ANI noise immunity parameters */ HAL_DIAG_CHECK_HANGS = 32, /* check h/w hangs */ HAL_DIAG_SETREGS = 33, /* write registers */ }; enum { HAL_BB_HANG_DFS = 0x0001, HAL_BB_HANG_RIFS = 0x0002, HAL_BB_HANG_RX_CLEAR = 0x0004, HAL_BB_HANG_UNKNOWN = 0x0080, HAL_MAC_HANG_SIG1 = 0x0100, HAL_MAC_HANG_SIG2 = 0x0200, HAL_MAC_HANG_UNKNOWN = 0x8000, HAL_BB_HANGS = HAL_BB_HANG_DFS | HAL_BB_HANG_RIFS | HAL_BB_HANG_RX_CLEAR | HAL_BB_HANG_UNKNOWN, HAL_MAC_HANGS = HAL_MAC_HANG_SIG1 | HAL_MAC_HANG_SIG2 | HAL_MAC_HANG_UNKNOWN, }; /* * Device revision information. */ typedef struct { uint16_t ah_devid; /* PCI device ID */ uint16_t ah_subvendorid; /* PCI subvendor ID */ uint32_t ah_macVersion; /* MAC version id */ uint16_t ah_macRev; /* MAC revision */ uint16_t ah_phyRev; /* PHY revision */ uint16_t ah_analog5GhzRev; /* 2GHz radio revision */ uint16_t ah_analog2GhzRev; /* 5GHz radio revision */ } HAL_REVS; /* * Argument payload for HAL_DIAG_SETKEY. */ typedef struct { HAL_KEYVAL dk_keyval; uint16_t dk_keyix; /* key index */ uint8_t dk_mac[IEEE80211_ADDR_LEN]; int dk_xor; /* XOR key data */ } HAL_DIAG_KEYVAL; /* * Argument payload for HAL_DIAG_EEWRITE. */ typedef struct { uint16_t ee_off; /* eeprom offset */ uint16_t ee_data; /* write data */ } HAL_DIAG_EEVAL; typedef struct { u_int offset; /* reg offset */ uint32_t val; /* reg value */ } HAL_DIAG_REGVAL; /* * 11n compatibility tweaks. */ #define HAL_DIAG_11N_SERVICES 0x00000003 #define HAL_DIAG_11N_SERVICES_S 0 #define HAL_DIAG_11N_TXSTOMP 0x0000000c #define HAL_DIAG_11N_TXSTOMP_S 2 typedef struct { int maxNoiseImmunityLevel; /* [0..4] */ int totalSizeDesired[5]; int coarseHigh[5]; int coarseLow[5]; int firpwr[5]; int maxSpurImmunityLevel; /* [0..7] */ int cycPwrThr1[8]; int maxFirstepLevel; /* [0..2] */ int firstep[3]; uint32_t ofdmTrigHigh; uint32_t ofdmTrigLow; int32_t cckTrigHigh; int32_t cckTrigLow; int32_t rssiThrLow; int32_t rssiThrHigh; int period; /* update listen period */ } HAL_ANI_PARAMS; extern HAL_BOOL ath_hal_getdiagstate(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); /* * Setup a h/w rate table for use. */ extern void ath_hal_setupratetable(struct ath_hal *ah, HAL_RATE_TABLE *rt); /* * Common routine for implementing getChanNoise api. */ int16_t ath_hal_getChanNoise(struct ath_hal *, const struct ieee80211_channel *); /* * Initialization support. */ typedef struct { const uint32_t *data; int rows, cols; } HAL_INI_ARRAY; #define HAL_INI_INIT(_ia, _data, _cols) do { \ (_ia)->data = (const uint32_t *)(_data); \ (_ia)->rows = sizeof(_data) / sizeof((_data)[0]); \ (_ia)->cols = (_cols); \ } while (0) #define HAL_INI_VAL(_ia, _r, _c) \ ((_ia)->data[((_r)*(_ia)->cols) + (_c)]) /* * OS_DELAY() does a PIO READ on the PCI bus which allows * other cards' DMA reads to complete in the middle of our reset. */ #define DMA_YIELD(x) do { \ if ((++(x) % 64) == 0) \ OS_DELAY(1); \ } while (0) #define HAL_INI_WRITE_ARRAY(ah, regArray, col, regWr) do { \ int r; \ for (r = 0; r < N(regArray); r++) { \ OS_REG_WRITE(ah, (regArray)[r][0], (regArray)[r][col]); \ DMA_YIELD(regWr); \ } \ } while (0) #define HAL_INI_WRITE_BANK(ah, regArray, bankData, regWr) do { \ int r; \ for (r = 0; r < N(regArray); r++) { \ OS_REG_WRITE(ah, (regArray)[r][0], (bankData)[r]); \ DMA_YIELD(regWr); \ } \ } while (0) extern int ath_hal_ini_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, int col, int regWr); extern void ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col); extern int ath_hal_ini_bank_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, const uint32_t data[], int regWr); #define CCK_SIFS_TIME 10 #define CCK_PREAMBLE_BITS 144 #define CCK_PLCP_BITS 48 #define OFDM_SIFS_TIME 16 #define OFDM_PREAMBLE_TIME 20 #define OFDM_PLCP_BITS 22 #define OFDM_SYMBOL_TIME 4 #define OFDM_HALF_SIFS_TIME 32 #define OFDM_HALF_PREAMBLE_TIME 40 #define OFDM_HALF_PLCP_BITS 22 #define OFDM_HALF_SYMBOL_TIME 8 #define OFDM_QUARTER_SIFS_TIME 64 #define OFDM_QUARTER_PREAMBLE_TIME 80 #define OFDM_QUARTER_PLCP_BITS 22 #define OFDM_QUARTER_SYMBOL_TIME 16 #define TURBO_SIFS_TIME 8 #define TURBO_PREAMBLE_TIME 14 #define TURBO_PLCP_BITS 22 #define TURBO_SYMBOL_TIME 4 #define WLAN_CTRL_FRAME_SIZE (2+2+6+4) /* ACK+FCS */ #endif /* _ATH_AH_INTERAL_H_ */ diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c b/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c index ace6989747e9..67fe36092b86 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c +++ b/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c @@ -1,888 +1,891 @@ /* * 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" #define AH_5212_COMMON #include "ar5212/ar5212.ini" static void ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); static void ar5212DisablePCIE(struct ath_hal *ah); static const struct ath_hal_private ar5212hal = {{ .ah_magic = AR5212_MAGIC, .ah_getRateTable = ar5212GetRateTable, .ah_detach = ar5212Detach, /* Reset Functions */ .ah_reset = ar5212Reset, .ah_phyDisable = ar5212PhyDisable, .ah_disable = ar5212Disable, .ah_configPCIE = ar5212ConfigPCIE, .ah_disablePCIE = ar5212DisablePCIE, .ah_setPCUConfig = ar5212SetPCUConfig, .ah_perCalibration = ar5212PerCalibration, .ah_perCalibrationN = ar5212PerCalibrationN, .ah_resetCalValid = ar5212ResetCalValid, .ah_setTxPowerLimit = ar5212SetTxPowerLimit, .ah_getChanNoise = ath_hal_getChanNoise, /* Transmit functions */ .ah_updateTxTrigLevel = ar5212UpdateTxTrigLevel, .ah_setupTxQueue = ar5212SetupTxQueue, .ah_setTxQueueProps = ar5212SetTxQueueProps, .ah_getTxQueueProps = ar5212GetTxQueueProps, .ah_releaseTxQueue = ar5212ReleaseTxQueue, .ah_resetTxQueue = ar5212ResetTxQueue, .ah_getTxDP = ar5212GetTxDP, .ah_setTxDP = ar5212SetTxDP, .ah_numTxPending = ar5212NumTxPending, .ah_startTxDma = ar5212StartTxDma, .ah_stopTxDma = ar5212StopTxDma, .ah_setupTxDesc = ar5212SetupTxDesc, .ah_setupXTxDesc = ar5212SetupXTxDesc, .ah_fillTxDesc = ar5212FillTxDesc, .ah_procTxDesc = ar5212ProcTxDesc, .ah_getTxIntrQueue = ar5212GetTxIntrQueue, .ah_reqTxIntrDesc = ar5212IntrReqTxDesc, /* RX Functions */ .ah_getRxDP = ar5212GetRxDP, .ah_setRxDP = ar5212SetRxDP, .ah_enableReceive = ar5212EnableReceive, .ah_stopDmaReceive = ar5212StopDmaReceive, .ah_startPcuReceive = ar5212StartPcuReceive, .ah_stopPcuReceive = ar5212StopPcuReceive, .ah_setMulticastFilter = ar5212SetMulticastFilter, .ah_setMulticastFilterIndex = ar5212SetMulticastFilterIndex, .ah_clrMulticastFilterIndex = ar5212ClrMulticastFilterIndex, .ah_getRxFilter = ar5212GetRxFilter, .ah_setRxFilter = ar5212SetRxFilter, .ah_setupRxDesc = ar5212SetupRxDesc, .ah_procRxDesc = ar5212ProcRxDesc, .ah_rxMonitor = ar5212AniPoll, .ah_procMibEvent = ar5212ProcessMibIntr, /* Misc Functions */ .ah_getCapability = ar5212GetCapability, .ah_setCapability = ar5212SetCapability, .ah_getDiagState = ar5212GetDiagState, .ah_getMacAddress = ar5212GetMacAddress, .ah_setMacAddress = ar5212SetMacAddress, .ah_getBssIdMask = ar5212GetBssIdMask, .ah_setBssIdMask = ar5212SetBssIdMask, .ah_setRegulatoryDomain = ar5212SetRegulatoryDomain, .ah_setLedState = ar5212SetLedState, .ah_writeAssocid = ar5212WriteAssocid, .ah_gpioCfgInput = ar5212GpioCfgInput, .ah_gpioCfgOutput = ar5212GpioCfgOutput, .ah_gpioGet = ar5212GpioGet, .ah_gpioSet = ar5212GpioSet, .ah_gpioSetIntr = ar5212GpioSetIntr, .ah_getTsf32 = ar5212GetTsf32, .ah_getTsf64 = ar5212GetTsf64, .ah_resetTsf = ar5212ResetTsf, .ah_detectCardPresent = ar5212DetectCardPresent, .ah_updateMibCounters = ar5212UpdateMibCounters, .ah_getRfGain = ar5212GetRfgain, .ah_getDefAntenna = ar5212GetDefAntenna, .ah_setDefAntenna = ar5212SetDefAntenna, .ah_getAntennaSwitch = ar5212GetAntennaSwitch, .ah_setAntennaSwitch = ar5212SetAntennaSwitch, .ah_setSifsTime = ar5212SetSifsTime, .ah_getSifsTime = ar5212GetSifsTime, .ah_setSlotTime = ar5212SetSlotTime, .ah_getSlotTime = ar5212GetSlotTime, .ah_setAckTimeout = ar5212SetAckTimeout, .ah_getAckTimeout = ar5212GetAckTimeout, .ah_setAckCTSRate = ar5212SetAckCTSRate, .ah_getAckCTSRate = ar5212GetAckCTSRate, .ah_setCTSTimeout = ar5212SetCTSTimeout, .ah_getCTSTimeout = ar5212GetCTSTimeout, .ah_setDecompMask = ar5212SetDecompMask, .ah_setCoverageClass = ar5212SetCoverageClass, /* Key Cache Functions */ .ah_getKeyCacheSize = ar5212GetKeyCacheSize, .ah_resetKeyCacheEntry = ar5212ResetKeyCacheEntry, .ah_isKeyCacheEntryValid = ar5212IsKeyCacheEntryValid, .ah_setKeyCacheEntry = ar5212SetKeyCacheEntry, .ah_setKeyCacheEntryMac = ar5212SetKeyCacheEntryMac, /* Power Management Functions */ .ah_setPowerMode = ar5212SetPowerMode, .ah_getPowerMode = ar5212GetPowerMode, /* Beacon Functions */ .ah_setBeaconTimers = ar5212SetBeaconTimers, .ah_beaconInit = ar5212BeaconInit, .ah_setStationBeaconTimers = ar5212SetStaBeaconTimers, .ah_resetStationBeaconTimers = ar5212ResetStaBeaconTimers, /* Interrupt Functions */ .ah_isInterruptPending = ar5212IsInterruptPending, .ah_getPendingInterrupts = ar5212GetPendingInterrupts, .ah_getInterrupts = ar5212GetInterrupts, .ah_setInterrupts = ar5212SetInterrupts }, + .ah_txtrig_level = INIT_TX_FIFO_THRESHOLD, + .ah_max_txtrig_level = MAX_TX_FIFO_THRESHOLD, + .ah_getChannelEdges = ar5212GetChannelEdges, .ah_getWirelessModes = ar5212GetWirelessModes, .ah_eepromRead = ar5212EepromRead, #ifdef AH_SUPPORT_WRITE_EEPROM .ah_eepromWrite = ar5212EepromWrite, #endif .ah_getChipPowerLimits = ar5212GetChipPowerLimits, }; uint32_t ar5212GetRadioRev(struct ath_hal *ah) { uint32_t val; int i; /* Read Radio Chip Rev Extract */ OS_REG_WRITE(ah, AR_PHY(0x34), 0x00001c16); for (i = 0; i < 8; i++) OS_REG_WRITE(ah, AR_PHY(0x20), 0x00010000); val = (OS_REG_READ(ah, AR_PHY(256)) >> 24) & 0xff; val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4); return ath_hal_reverseBits(val, 8); } static void ar5212AniSetup(struct ath_hal *ah) { static const struct ar5212AniParams aniparams = { .maxNoiseImmunityLevel = 4, /* levels 0..4 */ .totalSizeDesired = { -55, -55, -55, -55, -62 }, .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, .maxSpurImmunityLevel = 2, /* NB: depends on chip rev */ .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, .ofdmTrigLow = 200, .cckTrigHigh = 200, .cckTrigLow = 100, .rssiThrHigh = 40, .rssiThrLow = 7, .period = 100, }; if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_GRIFFIN) { struct ar5212AniParams tmp; OS_MEMCPY(&tmp, &aniparams, sizeof(struct ar5212AniParams)); tmp.maxSpurImmunityLevel = 7; /* Venice and earlier */ ar5212AniAttach(ah, &tmp, &tmp, AH_TRUE); } else ar5212AniAttach(ah, &aniparams, &aniparams, AH_TRUE); } /* * Attach for an AR5212 part. */ void ar5212InitState(struct ath_hal_5212 *ahp, uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS *status) { #define N(a) (sizeof(a)/sizeof(a[0])) static const uint8_t defbssidmask[IEEE80211_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; struct ath_hal *ah; ah = &ahp->ah_priv.h; /* set initial values */ OS_MEMCPY(&ahp->ah_priv, &ar5212hal, sizeof(struct ath_hal_private)); ah->ah_sc = sc; ah->ah_st = st; ah->ah_sh = sh; ah->ah_devid = devid; /* NB: for alq */ AH_PRIVATE(ah)->ah_devid = devid; AH_PRIVATE(ah)->ah_subvendorid = 0; /* XXX */ AH_PRIVATE(ah)->ah_powerLimit = MAX_RATE_POWER; AH_PRIVATE(ah)->ah_tpScale = HAL_TP_SCALE_MAX; /* no scaling */ ahp->ah_antControl = HAL_ANT_VARIABLE; ahp->ah_diversity = AH_TRUE; ahp->ah_bIQCalibration = AH_FALSE; /* * Enable MIC handling. */ ahp->ah_staId1Defaults = AR_STA_ID1_CRPT_MIC_ENABLE; ahp->ah_rssiThr = INIT_RSSI_THR; ahp->ah_tpcEnabled = AH_FALSE; /* disabled by default */ ahp->ah_phyPowerOn = AH_FALSE; ahp->ah_macTPC = SM(MAX_RATE_POWER, AR_TPC_ACK) | SM(MAX_RATE_POWER, AR_TPC_CTS) | SM(MAX_RATE_POWER, AR_TPC_CHIRP); ahp->ah_beaconInterval = 100; /* XXX [20..1000] */ ahp->ah_enable32kHzClock = DONT_USE_32KHZ;/* XXX */ ahp->ah_slottime = (u_int) -1; ahp->ah_acktimeout = (u_int) -1; ahp->ah_ctstimeout = (u_int) -1; ahp->ah_sifstime = (u_int) -1; OS_MEMCPY(&ahp->ah_bssidmask, defbssidmask, IEEE80211_ADDR_LEN); #undef N } /* * Validate MAC version and revision. */ static HAL_BOOL ar5212IsMacSupported(uint8_t macVersion, uint8_t macRev) { #define N(a) (sizeof(a)/sizeof(a[0])) static const struct { uint8_t version; uint8_t revMin, revMax; } macs[] = { { AR_SREV_VERSION_VENICE, AR_SREV_D2PLUS, AR_SREV_REVISION_MAX }, { AR_SREV_VERSION_GRIFFIN, AR_SREV_D2PLUS, AR_SREV_REVISION_MAX }, { AR_SREV_5413, AR_SREV_REVISION_MIN, AR_SREV_REVISION_MAX }, { AR_SREV_5424, AR_SREV_REVISION_MIN, AR_SREV_REVISION_MAX }, { AR_SREV_2425, AR_SREV_REVISION_MIN, AR_SREV_REVISION_MAX }, { AR_SREV_2417, AR_SREV_REVISION_MIN, AR_SREV_REVISION_MAX }, }; int i; for (i = 0; i < N(macs); i++) if (macs[i].version == macVersion && macs[i].revMin <= macRev && macRev <= macs[i].revMax) return AH_TRUE; return AH_FALSE; #undef N } /* * Attach for an AR5212 part. */ static struct ath_hal * ar5212Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS *status) { #define AH_EEPROM_PROTECT(ah) \ (AH_PRIVATE(ah)->ah_ispcie)? AR_EEPROM_PROTECT_PCIE : AR_EEPROM_PROTECT) struct ath_hal_5212 *ahp; struct ath_hal *ah; struct ath_hal_rf *rf; uint32_t val; uint16_t eeval; HAL_STATUS ecode; HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp = ath_hal_malloc(sizeof (struct ath_hal_5212)); if (ahp == AH_NULL) { HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; } ar5212InitState(ahp, devid, sc, st, sh, status); ah = &ahp->ah_priv.h; if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", __func__); ecode = HAL_EIO; goto bad; } /* Read Revisions from Chips before taking out of reset */ val = OS_REG_READ(ah, AR_SREV) & AR_SREV_ID; AH_PRIVATE(ah)->ah_macVersion = val >> AR_SREV_ID_S; AH_PRIVATE(ah)->ah_macRev = val & AR_SREV_REVISION; AH_PRIVATE(ah)->ah_ispcie = IS_5424(ah) || IS_2425(ah); if (!ar5212IsMacSupported(AH_PRIVATE(ah)->ah_macVersion, AH_PRIVATE(ah)->ah_macRev)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Mac Chip Rev 0x%02x.%x not supported\n" , __func__, AH_PRIVATE(ah)->ah_macVersion, AH_PRIVATE(ah)->ah_macRev); ecode = HAL_ENOTSUPP; goto bad; } /* setup common ini data; rf backends handle remainder */ HAL_INI_INIT(&ahp->ah_ini_modes, ar5212Modes, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar5212Common, 2); if (!ar5212ChipReset(ah, AH_NULL)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); ecode = HAL_EIO; goto bad; } AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); if (AH_PRIVATE(ah)->ah_ispcie) { /* XXX: build flag to disable this? */ ath_hal_configPCIE(ah, AH_FALSE); } if (!ar5212ChipTest(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", __func__); ecode = HAL_ESELFTEST; goto bad; } /* Enable PCI core retry fix in software for Hainan and up */ if (AH_PRIVATE(ah)->ah_macVersion >= AR_SREV_VERSION_VENICE) OS_REG_SET_BIT(ah, AR_PCICFG, AR_PCICFG_RETRYFIXEN); /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Read Radio Chip Rev Extract */ AH_PRIVATE(ah)->ah_analog5GhzRev = ar5212GetRadioRev(ah); rf = ath_hal_rfprobe(ah, &ecode); if (rf == AH_NULL) goto bad; /* NB: silently accept anything in release code per Atheros */ switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case AR_RAD5111_SREV_MAJOR: case AR_RAD5112_SREV_MAJOR: case AR_RAD2112_SREV_MAJOR: case AR_RAD2111_SREV_MAJOR: case AR_RAD2413_SREV_MAJOR: case AR_RAD5413_SREV_MAJOR: case AR_RAD5424_SREV_MAJOR: break; default: if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { /* * When RF_Silent is used, the * analog chip is reset. So when the system boots * up with the radio switch off we cannot determine * the RF chip rev. To workaround this check the * mac+phy revs and if Hainan, set the radio rev * to Derby. */ if (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_VENICE && AH_PRIVATE(ah)->ah_macRev == AR_SREV_HAINAN && AH_PRIVATE(ah)->ah_phyRev == AR_PHYREV_HAINAN) { AH_PRIVATE(ah)->ah_analog5GhzRev = AR_ANALOG5REV_HAINAN; break; } if (IS_2413(ah)) { /* Griffin */ AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD2413_SREV_MAJOR | 0x1; break; } if (IS_5413(ah)) { /* Eagle */ AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5413_SREV_MAJOR | 0x2; break; } if (IS_2425(ah) || IS_2417(ah)) {/* Swan or Nala */ AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5424_SREV_MAJOR | 0x2; break; } } #ifdef AH_DEBUG HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5G Radio Chip Rev 0x%02X is not supported by " "this driver\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; #endif } if (IS_RAD5112_REV1(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5112 Rev 1 is not supported by this " "driver (analog5GhzRev 0x%x)\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; } val = OS_REG_READ(ah, AR_PCICFG); val = MS(val, AR_PCICFG_EEPROM_SIZE); if (val == 0) { if (!AH_PRIVATE(ah)->ah_ispcie) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unsupported EEPROM size %u (0x%x) found\n", __func__, val, val); ecode = HAL_EESIZE; goto bad; } /* XXX AH_PRIVATE(ah)->ah_isPciExpress = AH_TRUE; */ } else if (val != AR_PCICFG_EEPROM_SIZE_16K) { if (AR_PCICFG_EEPROM_SIZE_FAILED == val) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unsupported EEPROM size %u (0x%x) found\n", __func__, val, val); ecode = HAL_EESIZE; goto bad; } HALDEBUG(ah, HAL_DEBUG_ANY, "%s: EEPROM size = %d. Must be %d (16k).\n", __func__, val, AR_PCICFG_EEPROM_SIZE_16K); ecode = HAL_EESIZE; goto bad; } ecode = ath_hal_legacyEepromAttach(ah); if (ecode != HAL_OK) { goto bad; } ahp->ah_isHb63 = IS_2425(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_ISTALON); /* * If Bmode and AR5212, verify 2.4 analog exists */ if (ath_hal_eepromGetFlag(ah, AR_EEP_BMODE) && (AH_PRIVATE(ah)->ah_analog5GhzRev & 0xF0) == AR_RAD5111_SREV_MAJOR) { /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00004007); OS_DELAY(2000); AH_PRIVATE(ah)->ah_analog2GhzRev = ar5212GetRadioRev(ah); /* Set baseband for 5GHz chip */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); OS_DELAY(2000); if ((AH_PRIVATE(ah)->ah_analog2GhzRev & 0xF0) != AR_RAD2111_SREV_MAJOR) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 2G Radio Chip Rev 0x%02X is not " "supported by this driver\n", __func__, AH_PRIVATE(ah)->ah_analog2GhzRev); ecode = HAL_ENOTSUPP; goto bad; } } ecode = ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, &eeval); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cannot read regulatory domain from EEPROM\n", __func__); goto bad; } AH_PRIVATE(ah)->ah_currentRD = eeval; /* XXX record serial number */ /* * Got everything we need now to setup the capabilities. */ if (!ar5212FillCapabilityInfo(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: failed ar5212FillCapabilityInfo\n", __func__); ecode = HAL_EEREAD; goto bad; } if (!rf->attach(ah, &ecode)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", __func__, ecode); goto bad; } /* * Set noise floor adjust method; we arrange a * direct call instead of thunking. */ AH_PRIVATE(ah)->ah_getNfAdjust = ahp->ah_rfHal->getNfAdjust; /* Initialize gain ladder thermal calibration structure */ ar5212InitializeGainValues(ah); ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error getting mac address from EEPROM\n", __func__); goto bad; } ar5212AniSetup(ah); /* Setup of Radar/AR structures happens in ath_hal_initchannels*/ ar5212InitNfCalHistBuffer(ah); /* XXX EAR stuff goes here */ HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); return ah; bad: if (ahp) ar5212Detach((struct ath_hal *) ahp); if (status) *status = ecode; return AH_NULL; #undef AH_EEPROM_PROTECT } void ar5212Detach(struct ath_hal *ah) { HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s:\n", __func__); HALASSERT(ah != AH_NULL); HALASSERT(ah->ah_magic == AR5212_MAGIC); ar5212AniDetach(ah); ar5212RfDetach(ah); ar5212Disable(ah); ar5212SetPowerMode(ah, HAL_PM_FULL_SLEEP, AH_TRUE); ath_hal_eepromDetach(ah); ath_hal_free(ah); } HAL_BOOL ar5212ChipTest(struct ath_hal *ah) { uint32_t regAddr[2] = { AR_STA_ID0, AR_PHY_BASE+(8 << 2) }; uint32_t regHold[2]; uint32_t patternData[4] = { 0x55555555, 0xaaaaaaaa, 0x66666666, 0x99999999 }; int i, j; /* Test PHY & MAC registers */ for (i = 0; i < 2; i++) { uint32_t addr = regAddr[i]; uint32_t wrData, rdData; regHold[i] = OS_REG_READ(ah, addr); for (j = 0; j < 0x100; j++) { wrData = (j << 16) | j; OS_REG_WRITE(ah, addr, wrData); rdData = OS_REG_READ(ah, addr); if (rdData != wrData) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: address test failed addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", __func__, addr, wrData, rdData); return AH_FALSE; } } for (j = 0; j < 4; j++) { wrData = patternData[j]; OS_REG_WRITE(ah, addr, wrData); rdData = OS_REG_READ(ah, addr); if (wrData != rdData) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: address test failed addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", __func__, addr, wrData, rdData); return AH_FALSE; } } OS_REG_WRITE(ah, regAddr[i], regHold[i]); } OS_DELAY(100); return AH_TRUE; } /* * Store the channel edges for the requested operational mode */ HAL_BOOL ar5212GetChannelEdges(struct ath_hal *ah, uint16_t flags, uint16_t *low, uint16_t *high) { if (flags & IEEE80211_CHAN_5GHZ) { *low = 4915; *high = 6100; return AH_TRUE; } if ((flags & IEEE80211_CHAN_2GHZ) && (ath_hal_eepromGetFlag(ah, AR_EEP_BMODE) || ath_hal_eepromGetFlag(ah, AR_EEP_GMODE))) { *low = 2312; *high = 2732; return AH_TRUE; } return AH_FALSE; } /* * Disable PLL when in L0s as well as receiver clock when in L1. * This power saving option must be enabled through the Serdes. * * Programming the Serdes must go through the same 288 bit serial shift * register as the other analog registers. Hence the 9 writes. * * XXX Clean up the magic numbers. */ static void ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore) { OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); /* RX shut off when elecidle is asserted */ OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); /* Shut off PLL and CLKREQ active in L1 */ OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007); /* Load the new settings */ OS_REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); } static void ar5212DisablePCIE(struct ath_hal *ah) { /* NB: fill in for 9100 */ } /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and * cannot be read. */ HAL_BOOL ar5212FillCapabilityInfo(struct ath_hal *ah) { #define AR_KEYTABLE_SIZE 128 #define IS_GRIFFIN_LITE(ah) \ (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_GRIFFIN && \ AH_PRIVATE(ah)->ah_macRev == AR_SREV_GRIFFIN_LITE) #define IS_COBRA(ah) \ (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_COBRA) #define IS_2112(ah) \ ((AH_PRIVATE(ah)->ah_analog5GhzRev & 0xF0) == AR_RAD2112_SREV_MAJOR) struct ath_hal_private *ahpriv = AH_PRIVATE(ah); HAL_CAPABILITIES *pCap = &ahpriv->ah_caps; uint16_t capField, val; /* Read the capability EEPROM location */ if (ath_hal_eepromGet(ah, AR_EEP_OPCAP, &capField) != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to read caps from eeprom\n", __func__); return AH_FALSE; } if (IS_2112(ah)) ath_hal_eepromSet(ah, AR_EEP_AMODE, AH_FALSE); if (capField == 0 && IS_GRIFFIN_LITE(ah)) { /* * For griffin-lite cards with unprogrammed capabilities. */ ath_hal_eepromSet(ah, AR_EEP_COMPRESS, AH_FALSE); ath_hal_eepromSet(ah, AR_EEP_FASTFRAME, AH_FALSE); ath_hal_eepromSet(ah, AR_EEP_TURBO5DISABLE, AH_TRUE); ath_hal_eepromSet(ah, AR_EEP_TURBO2DISABLE, AH_TRUE); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: override caps for griffin-lite, now 0x%x (+!turbo)\n", __func__, capField); } /* Modify reg domain on newer cards that need to work with older sw */ if (ahpriv->ah_opmode != HAL_M_HOSTAP && ahpriv->ah_subvendorid == AR_SUBVENDOR_ID_NEW_A) { if (ahpriv->ah_currentRD == 0x64 || ahpriv->ah_currentRD == 0x65) ahpriv->ah_currentRD += 5; else if (ahpriv->ah_currentRD == 0x41) ahpriv->ah_currentRD = 0x43; HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: regdomain mapped to 0x%x\n", __func__, ahpriv->ah_currentRD); } if (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_2417 || AH_PRIVATE(ah)->ah_macVersion == AR_SREV_2425) { HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: enable Bmode and disable turbo for Swan/Nala\n", __func__); ath_hal_eepromSet(ah, AR_EEP_BMODE, AH_TRUE); ath_hal_eepromSet(ah, AR_EEP_COMPRESS, AH_FALSE); ath_hal_eepromSet(ah, AR_EEP_FASTFRAME, AH_FALSE); ath_hal_eepromSet(ah, AR_EEP_TURBO5DISABLE, AH_TRUE); ath_hal_eepromSet(ah, AR_EEP_TURBO2DISABLE, AH_TRUE); } /* Construct wireless mode from EEPROM */ pCap->halWirelessModes = 0; if (ath_hal_eepromGetFlag(ah, AR_EEP_AMODE)) { pCap->halWirelessModes |= HAL_MODE_11A; if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO5DISABLE)) pCap->halWirelessModes |= HAL_MODE_TURBO; } if (ath_hal_eepromGetFlag(ah, AR_EEP_BMODE)) pCap->halWirelessModes |= HAL_MODE_11B; if (ath_hal_eepromGetFlag(ah, AR_EEP_GMODE) && ahpriv->ah_subvendorid != AR_SUBVENDOR_ID_NOG) { pCap->halWirelessModes |= HAL_MODE_11G; if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO2DISABLE)) pCap->halWirelessModes |= HAL_MODE_108G; } pCap->halLow2GhzChan = 2312; /* XXX 2417 too? */ if (IS_RAD5112_ANY(ah) || IS_5413(ah) || IS_2425(ah) || IS_2417(ah)) pCap->halHigh2GhzChan = 2500; else pCap->halHigh2GhzChan = 2732; pCap->halLow5GhzChan = 4915; pCap->halHigh5GhzChan = 6100; pCap->halCipherCkipSupport = AH_FALSE; pCap->halCipherTkipSupport = AH_TRUE; pCap->halCipherAesCcmSupport = (ath_hal_eepromGetFlag(ah, AR_EEP_AES) && ((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_VERSION_OAHU)))); pCap->halMicCkipSupport = AH_FALSE; pCap->halMicTkipSupport = AH_TRUE; pCap->halMicAesCcmSupport = ath_hal_eepromGetFlag(ah, AR_EEP_AES); /* * Starting with Griffin TX+RX mic keys can be combined * in one key cache slot. */ if (AH_PRIVATE(ah)->ah_macVersion >= AR_SREV_VERSION_GRIFFIN) pCap->halTkipMicTxRxKeySupport = AH_TRUE; else pCap->halTkipMicTxRxKeySupport = AH_FALSE; pCap->halChanSpreadSupport = AH_TRUE; pCap->halSleepAfterBeaconBroken = AH_TRUE; if (ahpriv->ah_macRev > 1 || IS_COBRA(ah)) { pCap->halCompressSupport = ath_hal_eepromGetFlag(ah, AR_EEP_COMPRESS) && (pCap->halWirelessModes & (HAL_MODE_11A|HAL_MODE_11G)) != 0; pCap->halBurstSupport = ath_hal_eepromGetFlag(ah, AR_EEP_BURST); pCap->halFastFramesSupport = ath_hal_eepromGetFlag(ah, AR_EEP_FASTFRAME) && (pCap->halWirelessModes & (HAL_MODE_11A|HAL_MODE_11G)) != 0; pCap->halChapTuningSupport = AH_TRUE; pCap->halTurboPrimeSupport = AH_TRUE; } pCap->halTurboGSupport = pCap->halWirelessModes & HAL_MODE_108G; pCap->halPSPollBroken = AH_TRUE; /* XXX fixed in later revs? */ pCap->halVEOLSupport = AH_TRUE; pCap->halBssIdMaskSupport = AH_TRUE; pCap->halMcastKeySrchSupport = AH_TRUE; if ((ahpriv->ah_macVersion == AR_SREV_VERSION_VENICE && ahpriv->ah_macRev == 8) || ahpriv->ah_macVersion > AR_SREV_VERSION_VENICE) pCap->halTsfAddSupport = AH_TRUE; if (ath_hal_eepromGet(ah, AR_EEP_MAXQCU, &val) == HAL_OK) pCap->halTotalQueues = val; else pCap->halTotalQueues = HAL_NUM_TX_QUEUES; if (ath_hal_eepromGet(ah, AR_EEP_KCENTRIES, &val) == HAL_OK) pCap->halKeyCacheSize = val; else pCap->halKeyCacheSize = AR_KEYTABLE_SIZE; pCap->halChanHalfRate = AH_TRUE; pCap->halChanQuarterRate = AH_TRUE; if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL) && ath_hal_eepromGet(ah, AR_EEP_RFSILENT, &ahpriv->ah_rfsilent) == HAL_OK) { /* NB: enabled by default */ ahpriv->ah_rfkillEnabled = AH_TRUE; pCap->halRfSilentSupport = AH_TRUE; } /* NB: this is a guess, noone seems to know the answer */ ahpriv->ah_rxornIsFatal = (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_VENICE); /* enable features that first appeared in Hainan */ if ((AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_VENICE && AH_PRIVATE(ah)->ah_macRev == AR_SREV_HAINAN) || AH_PRIVATE(ah)->ah_macVersion > AR_SREV_VERSION_VENICE) { /* h/w phy counters */ pCap->halHwPhyCounterSupport = AH_TRUE; /* bssid match disable */ pCap->halBssidMatchSupport = AH_TRUE; } pCap->halTstampPrecision = 15; pCap->halIntrMask = HAL_INT_COMMON | HAL_INT_RX | HAL_INT_TX | HAL_INT_FATAL | HAL_INT_BNR | HAL_INT_BMISC ; if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_GRIFFIN) pCap->halIntrMask &= ~HAL_INT_TBTT; return AH_TRUE; #undef IS_COBRA #undef IS_GRIFFIN_LITE #undef AR_KEYTABLE_SIZE } static const char* ar5212Probe(uint16_t vendorid, uint16_t devid) { if (vendorid == ATHEROS_VENDOR_ID || vendorid == ATHEROS_3COM_VENDOR_ID || vendorid == ATHEROS_3COM2_VENDOR_ID) { switch (devid) { case AR5212_FPGA: return "Atheros 5212 (FPGA)"; case AR5212_DEVID: case AR5212_DEVID_IBM: case AR5212_DEFAULT: return "Atheros 5212"; case AR5212_AR2413: return "Atheros 2413"; case AR5212_AR2417: return "Atheros 2417"; case AR5212_AR5413: return "Atheros 5413"; case AR5212_AR5424: return "Atheros 5424/2424"; } } return AH_NULL; } AH_CHIP(AR5212, ar5212Probe, ar5212Attach); diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c b/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c index ecdf34ebe46f..45404cf89cb3 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c +++ b/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c @@ -1,915 +1,921 @@ /* * 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 "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212desc.h" #include "ar5212/ar5212phy.h" #ifdef AH_SUPPORT_5311 #include "ar5212/ar5311reg.h" #endif #ifdef AH_NEED_DESC_SWAP static void ar5212SwapTxDesc(struct ath_desc *ds); #endif /* * Update Tx FIFO trigger level. * * Set bIncTrigLevel to TRUE to increase the trigger level. * Set bIncTrigLevel to FALSE to decrease the trigger level. * * Returns TRUE if the trigger level was updated */ HAL_BOOL ar5212UpdateTxTrigLevel(struct ath_hal *ah, HAL_BOOL bIncTrigLevel) { struct ath_hal_5212 *ahp = AH5212(ah); uint32_t txcfg, curLevel, newLevel; HAL_INT omask; + if (AH_PRIVATE(ah)->ah_txtrig_level >= + AH_PRIVATE(ah)->ah_max_txtrig_level) + return AH_FALSE; + /* * Disable interrupts while futzing with the fifo level. */ - omask = ar5212SetInterrupts(ah, ahp->ah_maskReg &~ HAL_INT_GLOBAL); + omask = ah->ah_setInterrupts(ah, ahp->ah_maskReg &~ HAL_INT_GLOBAL); txcfg = OS_REG_READ(ah, AR_TXCFG); curLevel = MS(txcfg, AR_FTRIG); newLevel = curLevel; if (bIncTrigLevel) { /* increase the trigger level */ - if (curLevel < MAX_TX_FIFO_THRESHOLD) + if (curLevel < AH_PRIVATE(ah)->ah_max_txtrig_level) newLevel++; } else if (curLevel > MIN_TX_FIFO_THRESHOLD) newLevel--; if (newLevel != curLevel) /* Update the trigger level */ OS_REG_WRITE(ah, AR_TXCFG, (txcfg &~ AR_FTRIG) | SM(newLevel, AR_FTRIG)); + AH_PRIVATE(ah)->ah_txtrig_level = newLevel; + /* re-enable chip interrupts */ - ar5212SetInterrupts(ah, omask); + ah->ah_setInterrupts(ah, omask); return (newLevel != curLevel); } /* * Set the properties of the tx queue with the parameters * from qInfo. */ HAL_BOOL ar5212SetTxQueueProps(struct ath_hal *ah, int q, const HAL_TXQ_INFO *qInfo) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; if (q >= pCap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } return ath_hal_setTxQProps(ah, &ahp->ah_txq[q], qInfo); } /* * Return the properties for the specified tx queue. */ HAL_BOOL ar5212GetTxQueueProps(struct ath_hal *ah, int q, HAL_TXQ_INFO *qInfo) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; if (q >= pCap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } return ath_hal_getTxQProps(ah, qInfo, &ahp->ah_txq[q]); } /* * Allocate and initialize a tx DCU/QCU combination. */ int ar5212SetupTxQueue(struct ath_hal *ah, HAL_TX_QUEUE type, const HAL_TXQ_INFO *qInfo) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_TX_QUEUE_INFO *qi; HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; int q, defqflags; /* by default enable OK+ERR+DESC+URN interrupts */ defqflags = HAL_TXQ_TXOKINT_ENABLE | HAL_TXQ_TXERRINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE | HAL_TXQ_TXURNINT_ENABLE; /* XXX move queue assignment to driver */ switch (type) { case HAL_TX_QUEUE_BEACON: q = pCap->halTotalQueues-1; /* highest priority */ defqflags |= HAL_TXQ_DBA_GATED | HAL_TXQ_CBR_DIS_QEMPTY | HAL_TXQ_ARB_LOCKOUT_GLOBAL | HAL_TXQ_BACKOFF_DISABLE; break; case HAL_TX_QUEUE_CAB: q = pCap->halTotalQueues-2; /* next highest priority */ defqflags |= HAL_TXQ_DBA_GATED | HAL_TXQ_CBR_DIS_QEMPTY | HAL_TXQ_CBR_DIS_BEMPTY | HAL_TXQ_ARB_LOCKOUT_GLOBAL | HAL_TXQ_BACKOFF_DISABLE; break; case HAL_TX_QUEUE_UAPSD: q = pCap->halTotalQueues-3; /* nextest highest priority */ if (ahp->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no available UAPSD tx queue\n", __func__); return -1; } break; case HAL_TX_QUEUE_DATA: for (q = 0; q < pCap->halTotalQueues; q++) if (ahp->ah_txq[q].tqi_type == HAL_TX_QUEUE_INACTIVE) break; if (q == pCap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no available tx queue\n", __func__); return -1; } break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad tx queue type %u\n", __func__, type); return -1; } HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: queue %u\n", __func__, q); qi = &ahp->ah_txq[q]; if (qi->tqi_type != HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: tx queue %u already active\n", __func__, q); return -1; } OS_MEMZERO(qi, sizeof(HAL_TX_QUEUE_INFO)); qi->tqi_type = type; if (qInfo == AH_NULL) { qi->tqi_qflags = defqflags; qi->tqi_aifs = INIT_AIFS; qi->tqi_cwmin = HAL_TXQ_USEDEFAULT; /* NB: do at reset */ qi->tqi_cwmax = INIT_CWMAX; qi->tqi_shretry = INIT_SH_RETRY; qi->tqi_lgretry = INIT_LG_RETRY; qi->tqi_physCompBuf = 0; } else { qi->tqi_physCompBuf = qInfo->tqi_compBuf; (void) ar5212SetTxQueueProps(ah, q, qInfo); } /* NB: must be followed by ar5212ResetTxQueue */ return q; } /* * Update the h/w interrupt registers to reflect a tx q's configuration. */ static void setTxQInterrupts(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi) { struct ath_hal_5212 *ahp = AH5212(ah); HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", __func__, ahp->ah_txOkInterruptMask, ahp->ah_txErrInterruptMask, ahp->ah_txDescInterruptMask, ahp->ah_txEolInterruptMask, ahp->ah_txUrnInterruptMask); OS_REG_WRITE(ah, AR_IMR_S0, SM(ahp->ah_txOkInterruptMask, AR_IMR_S0_QCU_TXOK) | SM(ahp->ah_txDescInterruptMask, AR_IMR_S0_QCU_TXDESC) ); OS_REG_WRITE(ah, AR_IMR_S1, SM(ahp->ah_txErrInterruptMask, AR_IMR_S1_QCU_TXERR) | SM(ahp->ah_txEolInterruptMask, AR_IMR_S1_QCU_TXEOL) ); OS_REG_RMW_FIELD(ah, AR_IMR_S2, AR_IMR_S2_QCU_TXURN, ahp->ah_txUrnInterruptMask); } /* * Free a tx DCU/QCU combination. */ HAL_BOOL ar5212ReleaseTxQueue(struct ath_hal *ah, u_int q) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; HAL_TX_QUEUE_INFO *qi; if (q >= pCap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } qi = &ahp->ah_txq[q]; if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue %u\n", __func__, q); return AH_FALSE; } HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: release queue %u\n", __func__, q); qi->tqi_type = HAL_TX_QUEUE_INACTIVE; ahp->ah_txOkInterruptMask &= ~(1 << q); ahp->ah_txErrInterruptMask &= ~(1 << q); ahp->ah_txDescInterruptMask &= ~(1 << q); ahp->ah_txEolInterruptMask &= ~(1 << q); ahp->ah_txUrnInterruptMask &= ~(1 << q); setTxQInterrupts(ah, qi); return AH_TRUE; } /* * Set the retry, aifs, cwmin/max, readyTime regs for specified queue * Assumes: * phwChannel has been set to point to the current channel */ HAL_BOOL ar5212ResetTxQueue(struct ath_hal *ah, u_int q) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; HAL_TX_QUEUE_INFO *qi; uint32_t cwMin, chanCwMin, value, qmisc, dmisc; if (q >= pCap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } qi = &ahp->ah_txq[q]; if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue %u\n", __func__, q); return AH_TRUE; /* XXX??? */ } HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: reset queue %u\n", __func__, q); if (qi->tqi_cwmin == HAL_TXQ_USEDEFAULT) { /* * Select cwmin according to channel type. * NB: chan can be NULL during attach */ if (chan && IEEE80211_IS_CHAN_B(chan)) chanCwMin = INIT_CWMIN_11B; else chanCwMin = INIT_CWMIN; /* make sure that the CWmin is of the form (2^n - 1) */ for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1) ; } else cwMin = qi->tqi_cwmin; /* set cwMin/Max and AIFS values */ OS_REG_WRITE(ah, AR_DLCL_IFS(q), SM(cwMin, AR_D_LCL_IFS_CWMIN) | SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX) | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS)); /* Set retry limit values */ OS_REG_WRITE(ah, AR_DRETRY_LIMIT(q), SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH) | SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG) | SM(qi->tqi_lgretry, AR_D_RETRY_LIMIT_FR_LG) | SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH) ); /* NB: always enable early termination on the QCU */ qmisc = AR_Q_MISC_DCU_EARLY_TERM_REQ | SM(AR_Q_MISC_FSP_ASAP, AR_Q_MISC_FSP); /* NB: always enable DCU to wait for next fragment from QCU */ dmisc = AR_D_MISC_FRAG_WAIT_EN; #ifdef AH_SUPPORT_5311 if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_OAHU) { /* Configure DCU to use the global sequence count */ dmisc |= AR5311_D_MISC_SEQ_NUM_CONTROL; } #endif /* multiqueue support */ if (qi->tqi_cbrPeriod) { OS_REG_WRITE(ah, AR_QCBRCFG(q), SM(qi->tqi_cbrPeriod,AR_Q_CBRCFG_CBR_INTERVAL) | SM(qi->tqi_cbrOverflowLimit, AR_Q_CBRCFG_CBR_OVF_THRESH)); qmisc = (qmisc &~ AR_Q_MISC_FSP) | AR_Q_MISC_FSP_CBR; if (qi->tqi_cbrOverflowLimit) qmisc |= AR_Q_MISC_CBR_EXP_CNTR_LIMIT; } if (qi->tqi_readyTime) { OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT) | AR_Q_RDYTIMECFG_ENA); } OS_REG_WRITE(ah, AR_DCHNTIME(q), SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) | (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0)); if (qi->tqi_readyTime && (qi->tqi_qflags & HAL_TXQ_RDYTIME_EXP_POLICY_ENABLE)) qmisc |= AR_Q_MISC_RDYTIME_EXP_POLICY; if (qi->tqi_qflags & HAL_TXQ_DBA_GATED) qmisc = (qmisc &~ AR_Q_MISC_FSP) | AR_Q_MISC_FSP_DBA_GATED; if (MS(qmisc, AR_Q_MISC_FSP) != AR_Q_MISC_FSP_ASAP) { /* * These are meangingful only when not scheduled asap. */ if (qi->tqi_qflags & HAL_TXQ_CBR_DIS_BEMPTY) qmisc |= AR_Q_MISC_CBR_INCR_DIS0; else qmisc &= ~AR_Q_MISC_CBR_INCR_DIS0; if (qi->tqi_qflags & HAL_TXQ_CBR_DIS_QEMPTY) qmisc |= AR_Q_MISC_CBR_INCR_DIS1; else qmisc &= ~AR_Q_MISC_CBR_INCR_DIS1; } if (qi->tqi_qflags & HAL_TXQ_BACKOFF_DISABLE) dmisc |= AR_D_MISC_POST_FR_BKOFF_DIS; if (qi->tqi_qflags & HAL_TXQ_FRAG_BURST_BACKOFF_ENABLE) dmisc |= AR_D_MISC_FRAG_BKOFF_EN; if (qi->tqi_qflags & HAL_TXQ_ARB_LOCKOUT_GLOBAL) dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, AR_D_MISC_ARB_LOCKOUT_CNTRL); else if (qi->tqi_qflags & HAL_TXQ_ARB_LOCKOUT_INTRA) dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_INTRA_FR, AR_D_MISC_ARB_LOCKOUT_CNTRL); if (qi->tqi_qflags & HAL_TXQ_IGNORE_VIRTCOL) dmisc |= SM(AR_D_MISC_VIR_COL_HANDLING_IGNORE, AR_D_MISC_VIR_COL_HANDLING); if (qi->tqi_qflags & HAL_TXQ_SEQNUM_INC_DIS) dmisc |= AR_D_MISC_SEQ_NUM_INCR_DIS; /* * Fillin type-dependent bits. Most of this can be * removed by specifying the queue parameters in the * driver; it's here for backwards compatibility. */ switch (qi->tqi_type) { case HAL_TX_QUEUE_BEACON: /* beacon frames */ qmisc |= AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_BEACON_USE | AR_Q_MISC_CBR_INCR_DIS1; dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, AR_D_MISC_ARB_LOCKOUT_CNTRL) | AR_D_MISC_BEACON_USE | AR_D_MISC_POST_FR_BKOFF_DIS; break; case HAL_TX_QUEUE_CAB: /* CAB frames */ /* * No longer Enable AR_Q_MISC_RDYTIME_EXP_POLICY, * There is an issue with the CAB Queue * not properly refreshing the Tx descriptor if * the TXE clear setting is used. */ qmisc |= AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_CBR_INCR_DIS1 | AR_Q_MISC_CBR_INCR_DIS0; if (!qi->tqi_readyTime) { /* * NB: don't set default ready time if driver * has explicitly specified something. This is * here solely for backwards compatibility. */ value = (ahp->ah_beaconInterval - (ath_hal_sw_beacon_response_time - ath_hal_dma_beacon_response_time) - ath_hal_additional_swba_backoff) * 1024; OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_ENA); } dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, AR_D_MISC_ARB_LOCKOUT_CNTRL); break; default: /* NB: silence compiler */ break; } OS_REG_WRITE(ah, AR_QMISC(q), qmisc); OS_REG_WRITE(ah, AR_DMISC(q), dmisc); /* Setup compression scratchpad buffer */ /* * XXX: calling this asynchronously to queue operation can * cause unexpected behavior!!! */ if (qi->tqi_physCompBuf) { HALASSERT(qi->tqi_type == HAL_TX_QUEUE_DATA || qi->tqi_type == HAL_TX_QUEUE_UAPSD); OS_REG_WRITE(ah, AR_Q_CBBS, (80 + 2*q)); OS_REG_WRITE(ah, AR_Q_CBBA, qi->tqi_physCompBuf); OS_REG_WRITE(ah, AR_Q_CBC, HAL_COMP_BUF_MAX_SIZE/1024); OS_REG_WRITE(ah, AR_Q0_MISC + 4*q, OS_REG_READ(ah, AR_Q0_MISC + 4*q) | AR_Q_MISC_QCU_COMP_EN); } /* * Always update the secondary interrupt mask registers - this * could be a new queue getting enabled in a running system or * hw getting re-initialized during a reset! * * Since we don't differentiate between tx interrupts corresponding * to individual queues - secondary tx mask regs are always unmasked; * tx interrupts are enabled/disabled for all queues collectively * using the primary mask reg */ if (qi->tqi_qflags & HAL_TXQ_TXOKINT_ENABLE) ahp->ah_txOkInterruptMask |= 1 << q; else ahp->ah_txOkInterruptMask &= ~(1 << q); if (qi->tqi_qflags & HAL_TXQ_TXERRINT_ENABLE) ahp->ah_txErrInterruptMask |= 1 << q; else ahp->ah_txErrInterruptMask &= ~(1 << q); if (qi->tqi_qflags & HAL_TXQ_TXDESCINT_ENABLE) ahp->ah_txDescInterruptMask |= 1 << q; else ahp->ah_txDescInterruptMask &= ~(1 << q); if (qi->tqi_qflags & HAL_TXQ_TXEOLINT_ENABLE) ahp->ah_txEolInterruptMask |= 1 << q; else ahp->ah_txEolInterruptMask &= ~(1 << q); if (qi->tqi_qflags & HAL_TXQ_TXURNINT_ENABLE) ahp->ah_txUrnInterruptMask |= 1 << q; else ahp->ah_txUrnInterruptMask &= ~(1 << q); setTxQInterrupts(ah, qi); return AH_TRUE; } /* * Get the TXDP for the specified queue */ uint32_t ar5212GetTxDP(struct ath_hal *ah, u_int q) { HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues); return OS_REG_READ(ah, AR_QTXDP(q)); } /* * Set the TxDP for the specified queue */ HAL_BOOL ar5212SetTxDP(struct ath_hal *ah, u_int q, uint32_t txdp) { HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues); HALASSERT(AH5212(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE); /* * Make sure that TXE is deasserted before setting the TXDP. If TXE * is still asserted, setting TXDP will have no effect. */ HALASSERT((OS_REG_READ(ah, AR_Q_TXE) & (1 << q)) == 0); OS_REG_WRITE(ah, AR_QTXDP(q), txdp); return AH_TRUE; } /* * Set Transmit Enable bits for the specified queue */ HAL_BOOL ar5212StartTxDma(struct ath_hal *ah, u_int q) { HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues); HALASSERT(AH5212(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE); HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: queue %u\n", __func__, q); /* Check to be sure we're not enabling a q that has its TXD bit set. */ HALASSERT((OS_REG_READ(ah, AR_Q_TXD) & (1 << q)) == 0); OS_REG_WRITE(ah, AR_Q_TXE, 1 << q); return AH_TRUE; } /* * Return the number of pending frames or 0 if the specified * queue is stopped. */ uint32_t ar5212NumTxPending(struct ath_hal *ah, u_int q) { uint32_t npend; HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues); HALASSERT(AH5212(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE); npend = OS_REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT; if (npend == 0) { /* * Pending frame count (PFC) can momentarily go to zero * while TXE remains asserted. In other words a PFC of * zero is not sufficient to say that the queue has stopped. */ if (OS_REG_READ(ah, AR_Q_TXE) & (1 << q)) npend = 1; /* arbitrarily return 1 */ } return npend; } /* * Stop transmit on the specified queue */ HAL_BOOL ar5212StopTxDma(struct ath_hal *ah, u_int q) { u_int i; u_int wait; HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues); HALASSERT(AH5212(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE); OS_REG_WRITE(ah, AR_Q_TXD, 1 << q); for (i = 1000; i != 0; i--) { if (ar5212NumTxPending(ah, q) == 0) break; OS_DELAY(100); /* XXX get actual value */ } #ifdef AH_DEBUG if (i == 0) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: queue %u DMA did not stop in 100 msec\n", __func__, q); HALDEBUG(ah, HAL_DEBUG_ANY, "%s: QSTS 0x%x Q_TXE 0x%x Q_TXD 0x%x Q_CBR 0x%x\n", __func__, OS_REG_READ(ah, AR_QSTS(q)), OS_REG_READ(ah, AR_Q_TXE), OS_REG_READ(ah, AR_Q_TXD), OS_REG_READ(ah, AR_QCBRCFG(q))); HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Q_MISC 0x%x Q_RDYTIMECFG 0x%x Q_RDYTIMESHDN 0x%x\n", __func__, OS_REG_READ(ah, AR_QMISC(q)), OS_REG_READ(ah, AR_QRDYTIMECFG(q)), OS_REG_READ(ah, AR_Q_RDYTIMESHDN)); } #endif /* AH_DEBUG */ /* 2413+ and up can kill packets at the PCU level */ if (ar5212NumTxPending(ah, q) && (IS_2413(ah) || IS_5413(ah) || IS_2425(ah) || IS_2417(ah))) { uint32_t tsfLow, j; HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: Num of pending TX Frames %d on Q %d\n", __func__, ar5212NumTxPending(ah, q), q); /* Kill last PCU Tx Frame */ /* TODO - save off and restore current values of Q1/Q2? */ for (j = 0; j < 2; j++) { tsfLow = OS_REG_READ(ah, AR_TSF_L32); OS_REG_WRITE(ah, AR_QUIET2, SM(100, AR_QUIET2_QUIET_PER) | SM(10, AR_QUIET2_QUIET_DUR)); OS_REG_WRITE(ah, AR_QUIET1, AR_QUIET1_QUIET_ENABLE | SM(tsfLow >> 10, AR_QUIET1_NEXT_QUIET)); if ((OS_REG_READ(ah, AR_TSF_L32) >> 10) == (tsfLow >> 10)) { break; } HALDEBUG(ah, HAL_DEBUG_ANY, "%s: TSF moved while trying to set quiet time " "TSF: 0x%08x\n", __func__, tsfLow); HALASSERT(j < 1); /* TSF shouldn't count twice or reg access is taking forever */ } OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_CHAN_IDLE); /* Allow the quiet mechanism to do its work */ OS_DELAY(200); OS_REG_CLR_BIT(ah, AR_QUIET1, AR_QUIET1_QUIET_ENABLE); /* Give at least 1 millisec more to wait */ wait = 100; /* Verify all transmit is dead */ while (ar5212NumTxPending(ah, q)) { if ((--wait) == 0) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Failed to stop Tx DMA in %d msec after killing last frame\n", __func__, wait); break; } OS_DELAY(10); } OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_CHAN_IDLE); } OS_REG_WRITE(ah, AR_Q_TXD, 0); return (i != 0); } /* * Descriptor Access Functions */ #define VALID_PKT_TYPES \ ((1<ah_txPowerIndexOffset ); if(txPower > 63) txPower=63; ads->ds_ctl0 = (pktLen & AR_FrameLen) | (txPower << AR_XmitPower_S) | (flags & HAL_TXDESC_VEOL ? AR_VEOL : 0) | (flags & HAL_TXDESC_CLRDMASK ? AR_ClearDestMask : 0) | SM(antMode, AR_AntModeXmit) | (flags & HAL_TXDESC_INTREQ ? AR_TxInterReq : 0) ; ads->ds_ctl1 = (type << AR_FrmType_S) | (flags & HAL_TXDESC_NOACK ? AR_NoAck : 0) | (comp << AR_CompProc_S) | (compicvLen << AR_CompICVLen_S) | (compivLen << AR_CompIVLen_S) ; ads->ds_ctl2 = SM(txTries0, AR_XmitDataTries0) | (flags & HAL_TXDESC_DURENA ? AR_DurUpdateEna : 0) ; ads->ds_ctl3 = (txRate0 << AR_XmitRate0_S) ; if (keyIx != HAL_TXKEYIX_INVALID) { /* XXX validate key index */ ads->ds_ctl1 |= SM(keyIx, AR_DestIdx); ads->ds_ctl0 |= AR_DestIdxValid; } if (flags & RTSCTS) { if (!isValidTxRate(rtsctsRate)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid rts/cts rate 0x%x\n", __func__, rtsctsRate); return AH_FALSE; } /* XXX validate rtsctsDuration */ ads->ds_ctl0 |= (flags & HAL_TXDESC_CTSENA ? AR_CTSEnable : 0) | (flags & HAL_TXDESC_RTSENA ? AR_RTSCTSEnable : 0) ; ads->ds_ctl2 |= SM(rtsctsDuration, AR_RTSCTSDuration); ads->ds_ctl3 |= (rtsctsRate << AR_RTSCTSRate_S); } return AH_TRUE; #undef RTSCTS } HAL_BOOL ar5212SetupXTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int txRate1, u_int txTries1, u_int txRate2, u_int txTries2, u_int txRate3, u_int txTries3) { struct ar5212_desc *ads = AR5212DESC(ds); if (txTries1) { HALASSERT(isValidTxRate(txRate1)); ads->ds_ctl2 |= SM(txTries1, AR_XmitDataTries1) | AR_DurUpdateEna ; ads->ds_ctl3 |= (txRate1 << AR_XmitRate1_S); } if (txTries2) { HALASSERT(isValidTxRate(txRate2)); ads->ds_ctl2 |= SM(txTries2, AR_XmitDataTries2) | AR_DurUpdateEna ; ads->ds_ctl3 |= (txRate2 << AR_XmitRate2_S); } if (txTries3) { HALASSERT(isValidTxRate(txRate3)); ads->ds_ctl2 |= SM(txTries3, AR_XmitDataTries3) | AR_DurUpdateEna ; ads->ds_ctl3 |= (txRate3 << AR_XmitRate3_S); } return AH_TRUE; } void ar5212IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *ds) { struct ar5212_desc *ads = AR5212DESC(ds); #ifdef AH_NEED_DESC_SWAP ads->ds_ctl0 |= __bswap32(AR_TxInterReq); #else ads->ds_ctl0 |= AR_TxInterReq; #endif } HAL_BOOL ar5212FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0) { struct ar5212_desc *ads = AR5212DESC(ds); HALASSERT((segLen &~ AR_BufLen) == 0); if (firstSeg) { /* * First descriptor, don't clobber xmit control data * setup by ar5212SetupTxDesc. */ ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_More); } else if (lastSeg) { /* !firstSeg && lastSeg */ /* * Last descriptor in a multi-descriptor frame, * copy the multi-rate transmit parameters from * the first frame for processing on completion. */ ads->ds_ctl0 = 0; ads->ds_ctl1 = segLen; #ifdef AH_NEED_DESC_SWAP ads->ds_ctl2 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl2); ads->ds_ctl3 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl3); #else ads->ds_ctl2 = AR5212DESC_CONST(ds0)->ds_ctl2; ads->ds_ctl3 = AR5212DESC_CONST(ds0)->ds_ctl3; #endif } else { /* !firstSeg && !lastSeg */ /* * Intermediate descriptor in a multi-descriptor frame. */ ads->ds_ctl0 = 0; ads->ds_ctl1 = segLen | AR_More; ads->ds_ctl2 = 0; ads->ds_ctl3 = 0; } ads->ds_txstatus0 = ads->ds_txstatus1 = 0; return AH_TRUE; } #ifdef AH_NEED_DESC_SWAP /* Swap transmit descriptor */ static __inline void ar5212SwapTxDesc(struct ath_desc *ds) { ds->ds_data = __bswap32(ds->ds_data); ds->ds_ctl0 = __bswap32(ds->ds_ctl0); ds->ds_ctl1 = __bswap32(ds->ds_ctl1); ds->ds_hw[0] = __bswap32(ds->ds_hw[0]); ds->ds_hw[1] = __bswap32(ds->ds_hw[1]); ds->ds_hw[2] = __bswap32(ds->ds_hw[2]); ds->ds_hw[3] = __bswap32(ds->ds_hw[3]); } #endif /* * Processing of HW TX descriptor. */ HAL_STATUS ar5212ProcTxDesc(struct ath_hal *ah, struct ath_desc *ds, struct ath_tx_status *ts) { struct ar5212_desc *ads = AR5212DESC(ds); #ifdef AH_NEED_DESC_SWAP if ((ads->ds_txstatus1 & __bswap32(AR_Done)) == 0) return HAL_EINPROGRESS; ar5212SwapTxDesc(ds); #else if ((ads->ds_txstatus1 & AR_Done) == 0) return HAL_EINPROGRESS; #endif /* Update software copies of the HW status */ ts->ts_seqnum = MS(ads->ds_txstatus1, AR_SeqNum); ts->ts_tstamp = MS(ads->ds_txstatus0, AR_SendTimestamp); ts->ts_status = 0; if ((ads->ds_txstatus0 & AR_FrmXmitOK) == 0) { if (ads->ds_txstatus0 & AR_ExcessiveRetries) ts->ts_status |= HAL_TXERR_XRETRY; if (ads->ds_txstatus0 & AR_Filtered) ts->ts_status |= HAL_TXERR_FILT; if (ads->ds_txstatus0 & AR_FIFOUnderrun) ts->ts_status |= HAL_TXERR_FIFO; } /* * Extract the transmit rate used and mark the rate as * ``alternate'' if it wasn't the series 0 rate. */ ts->ts_finaltsi = MS(ads->ds_txstatus1, AR_FinalTSIndex); switch (ts->ts_finaltsi) { case 0: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate0); break; case 1: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate1); break; case 2: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate2); break; case 3: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate3); break; } ts->ts_rssi = MS(ads->ds_txstatus1, AR_AckSigStrength); ts->ts_shortretry = MS(ads->ds_txstatus0, AR_RTSFailCnt); ts->ts_longretry = MS(ads->ds_txstatus0, AR_DataFailCnt); /* * The retry count has the number of un-acked tries for the * final series used. When doing multi-rate retry we must * fixup the retry count by adding in the try counts for * each series that was fully-processed. Beware that this * takes values from the try counts in the final descriptor. * These are not required by the hardware. We assume they * are placed there by the driver as otherwise we have no * access and the driver can't do the calculation because it * doesn't know the descriptor format. */ switch (ts->ts_finaltsi) { case 3: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries2); case 2: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries1); case 1: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries0); } ts->ts_virtcol = MS(ads->ds_txstatus0, AR_VirtCollCnt); ts->ts_antenna = (ads->ds_txstatus1 & AR_XmitAtenna ? 2 : 1); return HAL_OK; } /* * Determine which tx queues need interrupt servicing. */ void ar5212GetTxIntrQueue(struct ath_hal *ah, uint32_t *txqs) { struct ath_hal_5212 *ahp = AH5212(ah); *txqs &= ahp->ah_intrTxqs; ahp->ah_intrTxqs &= ~(*txqs); } diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c b/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c index 22ad3b77303a..2950817ed60f 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c @@ -1,2190 +1,2193 @@ /* * 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 "ah_eeprom_v14.h" #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ #define EEP_MINOR(_ah) \ (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) #define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) #define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3) /* Additional Time delay to wait after activiting the Base band */ #define BASE_ACTIVATE_DELAY 100 /* 100 usec */ #define PLL_SETTLE_DELAY 300 /* 300 usec */ #define RTC_PLL_SETTLE_DELAY 1000 /* 1 ms */ static void ar5416InitDMA(struct ath_hal *ah); static void ar5416InitBB(struct ath_hal *ah, const struct ieee80211_channel *); static void ar5416InitIMR(struct ath_hal *ah, HAL_OPMODE opmode); static void ar5416InitQoS(struct ath_hal *ah); static void ar5416InitUserSettings(struct ath_hal *ah); #if 0 static HAL_BOOL ar5416ChannelChange(struct ath_hal *, const struct ieee80211_channel *); #endif static void ar5416SetDeltaSlope(struct ath_hal *, const struct ieee80211_channel *); static HAL_BOOL ar5416SetResetPowerOn(struct ath_hal *ah); static HAL_BOOL ar5416SetReset(struct ath_hal *ah, int type); static void ar5416InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan); static HAL_BOOL ar5416SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *ratesArray, uint16_t cfgCtl, uint16_t AntennaReduction, uint16_t twiceMaxRegulatoryPower, uint16_t powerLimit); static HAL_BOOL ar5416SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset); static uint16_t ar5416GetMaxEdgePower(uint16_t freq, CAL_CTL_EDGES *pRdEdgesPower, HAL_BOOL is2GHz); static int16_t interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight); static void ar5416Set11nRegs(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ *pRawDataSet, uint8_t * bChans, uint16_t availPiers, uint16_t tPdGainOverlap, int16_t *pMinCalPower, uint16_t * pPdGainBoundaries, uint8_t * pPDADCValues, uint16_t numXpdGains); static HAL_BOOL getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize, uint16_t *indexL, uint16_t *indexR); static HAL_BOOL ar5416FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList, uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList); /* * 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 ar5416Reset(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; uint32_t saveDefAntenna, saveLedState; uint32_t macStaId1; uint16_t rfXpdGain[2]; HAL_STATUS ecode; uint32_t powerVal, rssiThrReg; uint32_t ackTpcPow, ctsTpcPow, chirpTpcPow; int i; OS_MARK(ah, AH_MARK_RESET, bChannelChange); /* Bring out of sleep mode */ if (!ar5416SetPowerMode(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_VER14_1); /* XXX Turn on fast channel change for 5416 */ /* * Preserve the bmiss rssi threshold and count threshold * across resets */ rssiThrReg = OS_REG_READ(ah, AR_RSSI_THR); /* If reg is zero, first time thru set to default val */ if (rssiThrReg == 0) rssiThrReg = INIT_RSSI_THR; /* * 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_MAC_LED) & (AR_MAC_LED_ASSOC | AR_MAC_LED_MODE | AR_MAC_LED_BLINK_THRESH_SEL | AR_MAC_LED_BLINK_SLOW); if (!ar5416ChipReset(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); FAIL(HAL_EIO); } /* Restore bmiss rssi & count thresholds */ OS_REG_WRITE(ah, AR_RSSI_THR, rssiThrReg); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); AH5416(ah)->ah_writeIni(ah, chan); /* Setup 11n MAC/Phy mode registers */ ar5416Set11nRegs(ah, chan); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); HALDEBUG(ah, HAL_DEBUG_RESET, ">>>2 %s: AR_PHY_DAG_CTRLCCK=0x%x\n", __func__, OS_REG_READ(ah,AR_PHY_DAG_CTRLCCK)); HALDEBUG(ah, HAL_DEBUG_RESET, ">>>2 %s: AR_PHY_ADC_CTL=0x%x\n", __func__, OS_REG_READ(ah,AR_PHY_ADC_CTL)); /* Set the mute mask to the correct default */ if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2) 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 (AH5416(ah)->ah_rx_chainmask == 0x5 || AH5416(ah)->ah_tx_chainmask == 0x5) OS_REG_WRITE(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); /* Setup Chain Masks */ OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, AH5416(ah)->ah_rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, AH5416(ah)->ah_rx_chainmask); OS_REG_WRITE(ah, AR_SELFGEN_MASK, AH5416(ah)->ah_tx_chainmask); /* Setup the transmit power values. */ if (!ah->ah_setTxPower(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, IEEE80211_IS_CHAN_2GHZ(chan) ? 2: 1, 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)|| IEEE80211_IS_CHAN_HT(chan)) ar5416SetDeltaSlope(ah, chan); AH5416(ah)->ah_spurMitigate(ah, chan); /* Setup board specific options for EEPROM version 3 */ if (!ah->ah_setBoardValues(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error setting board options\n", __func__); FAIL(HAL_EIO); } 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_MAC_LED, OS_REG_READ(ah, AR_MAC_LED) | 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__); /* 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); ar5416InitIMR(ah, opmode); ar5212SetCoverageClass(ah, AH_PRIVATE(ah)->ah_coverageClass, 1); ar5416InitQoS(ah); ar5416InitUserSettings(ah); /* * disable seq number generation in hw */ OS_REG_WRITE(ah, AR_STA_ID1, OS_REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PRESERVE_SEQNUM); ar5416InitDMA(ah); /* * program OBS bus to see MAC interrupts */ OS_REG_WRITE(ah, AR_OBS, 8); #ifdef AR5416_INT_MITIGATION OS_REG_WRITE(ah, AR_MIRT, 0); OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500); OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 2000); #endif ar5416InitBB(ah, chan); /* Setup compression registers */ ar5212SetCompRegs(ah); /* XXX not needed? */ /* * 5416 baseband will check the per rate power table * and select the lower of the two */ ackTpcPow = 63; ctsTpcPow = 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); if (!ar5416InitCal(ah, chan)) FAIL(HAL_ESELFTEST); 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 } #if 0 /* * 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 ar5416ChannelChange(struct ath_hal *ah, const structu ieee80211_channel *chan) { uint32_t ulCount; uint32_t data, synthDelay, qnum; uint16_t rfXpdGain[4]; struct ath_hal_5212 *ahp = AH5212(ah); 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)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: frames pending on queue %d\n", __func__, qnum); return AH_FALSE; } } /* * Kill last Baseband Rx Frame - Request analog bus grant */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_REQUEST); if (!ath_hal_wait(ah, AR_PHY_RFBUS_GNT, AR_PHY_RFBUS_GRANT_EN, AR_PHY_RFBUS_GRANT_EN)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: could not kill baseband rx\n", __func__); return AH_FALSE; } ar5416Set11nRegs(ah, chan); /* NB: setup 5416-specific regs */ /* Change the synth */ if (!ar5212SetChannel(ah, chan)) return AH_FALSE; /* Setup the transmit power values. */ if (!ar5416SetTransmitPower(ah, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); 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 (IS_CHAN_CCK(ichan)) { synthDelay = (4 * data) / 22; } else { synthDelay = data / 10; } OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY); /* Release the RFBus Grant */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(ichan)|| IEEE80211_IS_CHAN_HT(chan)) { HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER5_3); ar5212SetSpurMitigation(ah, chan); ar5416SetDeltaSlope(ah, chan); } /* XXX spur mitigation for Melin */ if (!IEEE80211_IS_CHAN_DFS(chan)) chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT; ichan->channel_time = 0; ichan->tsf_last = ar5212GetTsf64(ah); ar5212TxEnable(ah, AH_TRUE); return AH_TRUE; } #endif static void ar5416InitDMA(struct ath_hal *ah) { /* * set AHB_MODE not to do cacheline prefetches */ OS_REG_SET_BIT(ah, AR_AHB_MODE, AR_AHB_PREFETCH_RD_EN); /* * let mac dma reads be in 128 byte chunks */ OS_REG_WRITE(ah, AR_TXCFG, (OS_REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK) | AR_TXCFG_DMASZ_128B); /* * let mac dma writes be in 128 byte chunks */ OS_REG_WRITE(ah, AR_RXCFG, (OS_REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK) | AR_RXCFG_DMASZ_128B); - /* XXX restore TX trigger level */ + /* restore TX trigger level */ + OS_REG_WRITE(ah, AR_TXCFG, + (OS_REG_READ(ah, AR_TXCFG) &~ AR_FTRIG) | + SM(AH_PRIVATE(ah)->ah_txtrig_level, AR_FTRIG)); /* * Setup receive FIFO threshold to hold off TX activities */ OS_REG_WRITE(ah, AR_RXFIFO_CFG, 0x200); /* * reduce the number of usable entries in PCU TXBUF to avoid * wrap around. */ OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_PCU_TXBUF_CTRL_USABLE_SIZE); } static void ar5416InitBB(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t synthDelay; /* * 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_CCK(chan)) { synthDelay = (4 * synthDelay) / 22; } else { synthDelay /= 10; } /* Turn on PLL on 5416 */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s %s channel\n", __func__, IEEE80211_IS_CHAN_5GHZ(chan) ? "5GHz" : "2GHz"); ar5416InitPLL(ah, chan); /* Activate the PHY (includes baseband activate and synthesizer on) */ OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); /* * If the AP starts the calibration before the base band timeout * completes we could get rx_clear false triggering. Add 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); } } static void ar5416InitIMR(struct ath_hal *ah, HAL_OPMODE opmode) { struct ath_hal_5212 *ahp = AH5212(ah); /* * 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_TXERR | AR_IMR_TXURN | AR_IMR_RXERR | AR_IMR_RXORN | AR_IMR_BCNMISC; #ifdef AR5416_INT_MITIGATION ahp->ah_maskReg |= AR_IMR_TXINTM | AR_IMR_RXINTM | AR_IMR_TXMINTR | AR_IMR_RXMINTR; #else ahp->ah_maskReg |= AR_IMR_TXOK | AR_IMR_RXOK; #endif 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 */ #if 0 OS_REG_WRITE(ah, AR_IMR_S2, OS_REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT | AR_IMR_S2_CST); #endif } static void ar5416InitQoS(struct ath_hal *ah) { /* QoS support */ 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)); /* * initialize TXOP for all TIDs */ OS_REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL); OS_REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); } static void ar5416InitUserSettings(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); /* 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); #if 0 /* XXX Todo */ if (ahp->ah_globaltxtimeout != (u_int) -1) ar5416SetGlobalTxTimeout(ah, ahp->ah_globaltxtimeout); #endif } /* * Places the hardware into reset and then pulls it out of reset */ HAL_BOOL ar5416ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan) { OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0); /* * Warm reset is optimistic. */ if (AR_SREV_MERLIN_20_OR_LATER(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) return AH_FALSE; } else { if (!ar5416SetResetReg(ah, HAL_RESET_WARM)) return AH_FALSE; } /* Bring out of sleep mode (AGAIN) */ if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; ar5416InitPLL(ah, chan); /* * 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. */ if (chan != AH_NULL) { uint32_t rfMode; /* treat channel B as channel G , no B mode suport in owl */ rfMode = IEEE80211_IS_CHAN_CCK(chan) ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; if (AR_SREV_MERLIN_20(ah) && IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { /* phy mode bits for 5GHz channels require Fast Clock */ rfMode |= AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE; } else if (!AR_SREV_MERLIN_10_OR_LATER(ah)) { rfMode |= IEEE80211_IS_CHAN_5GHZ(chan) ? AR_PHY_MODE_RF5GHZ : AR_PHY_MODE_RF2GHZ; } OS_REG_WRITE(ah, AR_PHY_MODE, rfMode); } return AH_TRUE; } /* * Delta slope coefficient computation. * Required for OFDM operation. */ static void ar5416GetDeltaSlopeValues(struct ath_hal *ah, uint32_t coef_scaled, uint32_t *coef_mantissa, uint32_t *coef_exponent) { #define COEF_SCALE_S 24 uint32_t coef_exp, coef_man; /* * 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)); *coef_mantissa = coef_man >> (COEF_SCALE_S - coef_exp); *coef_exponent = coef_exp - 16; #undef COEF_SCALE_S } void ar5416SetDeltaSlope(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define INIT_CLOCKMHZSCALED 0x64000000 uint32_t coef_scaled, ds_coef_exp, ds_coef_man; uint32_t clockMhzScaled; CHAN_CENTERS centers; /* half and quarter rate can divide the scaled clock by 2 or 4 respectively */ /* scale for selected channel bandwidth */ clockMhzScaled = INIT_CLOCKMHZSCALED; if (IEEE80211_IS_CHAN_TURBO(chan)) clockMhzScaled <<= 1; else if (IEEE80211_IS_CHAN_HALF(chan)) clockMhzScaled >>= 1; else if (IEEE80211_IS_CHAN_QUARTER(chan)) clockMhzScaled >>= 2; /* * ALGO -> coef = 1e8/fcarrier*fclock/40; * scaled coef to provide precision for this floating calculation */ ar5416GetChannelCenters(ah, chan, ¢ers); coef_scaled = clockMhzScaled / centers.synth_center; ar5416GetDeltaSlopeValues(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); 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); /* * For Short GI, * scaled coeff is 9/10 that of normal coeff */ coef_scaled = (9 * coef_scaled)/10; ar5416GetDeltaSlopeValues(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); /* for short gi */ OS_REG_RMW_FIELD(ah, AR_PHY_HALFGI, AR_PHY_HALFGI_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_HALFGI, AR_PHY_HALFGI_DSC_EXP, ds_coef_exp); #undef INIT_CLOCKMHZSCALED } /* * 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 ar5416SetTxPowerLimit(struct ath_hal *ah, uint32_t limit) { uint16_t dummyXpdGains[2]; AH_PRIVATE(ah)->ah_powerLimit = AH_MIN(limit, MAX_RATE_POWER); return ar5416SetTransmitPower(ah, AH_PRIVATE(ah)->ah_curchan, dummyXpdGains); } HAL_BOOL ar5416GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); 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 = AR5416_MAX_RATE_POWER; chan->ic_minpower = 0; } HALDEBUG(ah, HAL_DEBUG_RESET, "Chan %d: MaxPow = %d MinPow = %d\n", chan->ic_freq, chan->ic_maxpower, chan->ic_minpower); return AH_TRUE; } /* XXX gag, this is sick */ typedef enum Ar5416_Rates { rate6mb, rate9mb, rate12mb, rate18mb, rate24mb, rate36mb, rate48mb, rate54mb, rate1l, rate2l, rate2s, rate5_5l, rate5_5s, rate11l, rate11s, rateXr, rateHt20_0, rateHt20_1, rateHt20_2, rateHt20_3, rateHt20_4, rateHt20_5, rateHt20_6, rateHt20_7, rateHt40_0, rateHt40_1, rateHt40_2, rateHt40_3, rateHt40_4, rateHt40_5, rateHt40_6, rateHt40_7, rateDupCck, rateDupOfdm, rateExtCck, rateExtOfdm, Ar5416RateSize } AR5416_RATES; /************************************************************** * ar5416SetTransmitPower * * Set the transmit power in the baseband for the given * operating channel and mode. */ HAL_BOOL ar5416SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { #define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) #define N(a) (sizeof (a) / sizeof (a[0])) MODAL_EEP_HEADER *pModal; struct ath_hal_5212 *ahp = AH5212(ah); int16_t ratesArray[Ar5416RateSize]; int16_t txPowerIndexOffset = 0; uint8_t ht40PowerIncForPdadc = 2; int i; uint16_t cfgCtl; uint16_t powerLimit; uint16_t twiceAntennaReduction; uint16_t twiceMaxRegulatoryPower; int16_t maxPower; HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom; struct ar5416eeprom *pEepData = &ee->ee_base; HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); /* Setup info for the actual eeprom */ OS_MEMZERO(ratesArray, sizeof(ratesArray)); cfgCtl = ath_hal_getctl(ah, chan); powerLimit = chan->ic_maxregpower * 2; twiceAntennaReduction = chan->ic_maxantgain; twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); pModal = &pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)]; HALDEBUG(ah, HAL_DEBUG_RESET, "%s Channel=%u CfgCtl=%u\n", __func__,chan->ic_freq, cfgCtl ); if (IS_EEP_MINOR_V2(ah)) { ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; } if (!ar5416SetPowerPerRateTable(ah, pEepData, chan, &ratesArray[0],cfgCtl, twiceAntennaReduction, twiceMaxRegulatoryPower, powerLimit)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set tx power per rate table\n", __func__); return AH_FALSE; } if (!ar5416SetPowerCalTable(ah, pEepData, chan, &txPowerIndexOffset)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set power table\n", __func__); return AH_FALSE; } maxPower = AH_MAX(ratesArray[rate6mb], ratesArray[rateHt20_0]); if (IEEE80211_IS_CHAN_2GHZ(chan)) { maxPower = AH_MAX(maxPower, ratesArray[rate1l]); } if (IEEE80211_IS_CHAN_HT40(chan)) { maxPower = AH_MAX(maxPower, ratesArray[rateHt40_0]); } ahp->ah_tx6PowerInHalfDbm = maxPower; AH_PRIVATE(ah)->ah_maxPowerLevel = maxPower; ahp->ah_txPowerIndexOffset = txPowerIndexOffset; /* * txPowerIndexOffset is set by the SetPowerTable() call - * adjust the rate table (0 offset if rates EEPROM not loaded) */ for (i = 0; i < N(ratesArray); i++) { ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]); if (ratesArray[i] > AR5416_MAX_RATE_POWER) ratesArray[i] = AR5416_MAX_RATE_POWER; } #ifdef AH_EEPROM_DUMP ar5416PrintPowerPerRate(ah, ratesArray); #endif /* Write the OFDM power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, POW_SM(ratesArray[rate18mb], 24) | POW_SM(ratesArray[rate12mb], 16) | POW_SM(ratesArray[rate9mb], 8) | POW_SM(ratesArray[rate6mb], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, POW_SM(ratesArray[rate54mb], 24) | POW_SM(ratesArray[rate48mb], 16) | POW_SM(ratesArray[rate36mb], 8) | POW_SM(ratesArray[rate24mb], 0) ); if (IEEE80211_IS_CHAN_2GHZ(chan)) { /* Write the CCK power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, POW_SM(ratesArray[rate2s], 24) | POW_SM(ratesArray[rate2l], 16) | POW_SM(ratesArray[rateXr], 8) /* XR target power */ | POW_SM(ratesArray[rate1l], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, POW_SM(ratesArray[rate11s], 24) | POW_SM(ratesArray[rate11l], 16) | POW_SM(ratesArray[rate5_5s], 8) | POW_SM(ratesArray[rate5_5l], 0) ); HALDEBUG(ah, HAL_DEBUG_RESET, "%s AR_PHY_POWER_TX_RATE3=0x%x AR_PHY_POWER_TX_RATE4=0x%x\n", __func__, OS_REG_READ(ah,AR_PHY_POWER_TX_RATE3), OS_REG_READ(ah,AR_PHY_POWER_TX_RATE4)); } /* Write the HT20 power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE5, POW_SM(ratesArray[rateHt20_3], 24) | POW_SM(ratesArray[rateHt20_2], 16) | POW_SM(ratesArray[rateHt20_1], 8) | POW_SM(ratesArray[rateHt20_0], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE6, POW_SM(ratesArray[rateHt20_7], 24) | POW_SM(ratesArray[rateHt20_6], 16) | POW_SM(ratesArray[rateHt20_5], 8) | POW_SM(ratesArray[rateHt20_4], 0) ); if (IEEE80211_IS_CHAN_HT40(chan)) { /* Write the HT40 power per rate set */ /* Correct PAR difference between HT40 and HT20/LEGACY */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, POW_SM(ratesArray[rateHt40_3] + ht40PowerIncForPdadc, 24) | POW_SM(ratesArray[rateHt40_2] + ht40PowerIncForPdadc, 16) | POW_SM(ratesArray[rateHt40_1] + ht40PowerIncForPdadc, 8) | POW_SM(ratesArray[rateHt40_0] + ht40PowerIncForPdadc, 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, POW_SM(ratesArray[rateHt40_7] + ht40PowerIncForPdadc, 24) | POW_SM(ratesArray[rateHt40_6] + ht40PowerIncForPdadc, 16) | POW_SM(ratesArray[rateHt40_5] + ht40PowerIncForPdadc, 8) | POW_SM(ratesArray[rateHt40_4] + ht40PowerIncForPdadc, 0) ); /* Write the Dup/Ext 40 power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, POW_SM(ratesArray[rateExtOfdm], 24) | POW_SM(ratesArray[rateExtCck], 16) | POW_SM(ratesArray[rateDupOfdm], 8) | POW_SM(ratesArray[rateDupCck], 0) ); } /* Write the Power subtraction for dynamic chain changing, for per-packet powertx */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_SUB, POW_SM(pModal->pwrDecreaseFor3Chain, 6) | POW_SM(pModal->pwrDecreaseFor2Chain, 0) ); return AH_TRUE; #undef POW_SM #undef N } /* * Exported call to check for a recent gain reading and return * the current state of the thermal calibration gain engine. */ HAL_RFGAIN ar5416GetRfgain(struct ath_hal *ah) { return HAL_RFGAIN_INACTIVE; } /* * Places all of hardware into reset */ HAL_BOOL ar5416Disable(struct ath_hal *ah) { if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; return ar5416SetResetReg(ah, HAL_RESET_COLD); } /* * 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 ar5416PhyDisable(struct ath_hal *ah) { return ar5416SetResetReg(ah, HAL_RESET_WARM); } /* * Write the given reset bit mask into the reset register */ HAL_BOOL ar5416SetResetReg(struct ath_hal *ah, uint32_t type) { switch (type) { case HAL_RESET_POWER_ON: return ar5416SetResetPowerOn(ah); case HAL_RESET_WARM: case HAL_RESET_COLD: return ar5416SetReset(ah, type); default: HALASSERT(AH_FALSE); return AH_FALSE; } } static HAL_BOOL ar5416SetResetPowerOn(struct ath_hal *ah) { /* Power On Reset (Hard Reset) */ /* * Set force wake * * If the MAC was running, previously calling * reset will wake up the MAC but it may go back to sleep * before we can start polling. * Set force wake stops that * This must be called before initiating a hard reset. */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); /* * RTC reset and clear */ OS_REG_WRITE(ah, AR_RTC_RESET, 0); OS_DELAY(20); OS_REG_WRITE(ah, AR_RTC_RESET, 1); /* * Poll till RTC is ON */ if (!ath_hal_wait(ah, AR_RTC_STATUS, AR_RTC_PM_STATUS_M, AR_RTC_STATUS_ON)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RTC not waking up\n", __func__); return AH_FALSE; } return ar5416SetReset(ah, HAL_RESET_COLD); } static HAL_BOOL ar5416SetReset(struct ath_hal *ah, int type) { uint32_t tmpReg, mask; /* * Force wake */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); /* * Reset AHB */ tmpReg = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE); if (tmpReg & (AR_INTR_SYNC_LOCAL_TIMEOUT|AR_INTR_SYNC_RADM_CPL_TIMEOUT)) { OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0); OS_REG_WRITE(ah, AR_RC, AR_RC_AHB|AR_RC_HOSTIF); } else { OS_REG_WRITE(ah, AR_RC, AR_RC_AHB); } /* * Set Mac(BB,Phy) Warm Reset */ switch (type) { case HAL_RESET_WARM: OS_REG_WRITE(ah, AR_RTC_RC, AR_RTC_RC_MAC_WARM); break; case HAL_RESET_COLD: OS_REG_WRITE(ah, AR_RTC_RC, AR_RTC_RC_MAC_WARM|AR_RTC_RC_MAC_COLD); break; default: HALASSERT(AH_FALSE); break; } /* * Clear resets and force wakeup */ OS_REG_WRITE(ah, AR_RTC_RC, 0); if (!ath_hal_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RTC stuck in MAC reset\n", __func__); return AH_FALSE; } /* Clear AHB reset */ OS_REG_WRITE(ah, AR_RC, 0); if (type == HAL_RESET_COLD) { 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 HALDEBUG(ah, HAL_DEBUG_RESET, "%s Applying descriptor swap\n", __func__); OS_REG_WRITE(ah, AR_CFG, LE_READ_4(&mask)); } else OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS); } ar5416InitPLL(ah, AH_NULL); return AH_TRUE; } #ifndef IS_5GHZ_FAST_CLOCK_EN #define IS_5GHZ_FAST_CLOCK_EN(ah, chan) AH_FALSE #endif static void ar5416InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t pll; if (AR_SREV_MERLIN_20(ah) && chan != AH_NULL && IEEE80211_IS_CHAN_5GHZ(chan)) { /* * PLL WAR for Merlin 2.0/2.1 * When doing fast clock, set PLL to 0x142c * Else, set PLL to 0x2850 to prevent reset-to-reset variation */ pll = IS_5GHZ_FAST_CLOCK_EN(ah, chan) ? 0x142c : 0x2850; } else if (AR_SREV_MERLIN_10_OR_LATER(ah)) { pll = SM(0x5, AR_RTC_SOWL_PLL_REFDIV); if (chan != AH_NULL) { if (IEEE80211_IS_CHAN_HALF(chan)) pll |= SM(0x1, AR_RTC_SOWL_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_QUARTER(chan)) pll |= SM(0x2, AR_RTC_SOWL_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_5GHZ(chan)) pll |= SM(0x28, AR_RTC_SOWL_PLL_DIV); else pll |= SM(0x2c, AR_RTC_SOWL_PLL_DIV); } else pll |= SM(0x2c, AR_RTC_SOWL_PLL_DIV); } else if (AR_SREV_SOWL_10_OR_LATER(ah)) { pll = SM(0x5, AR_RTC_SOWL_PLL_REFDIV); if (chan != AH_NULL) { if (IEEE80211_IS_CHAN_HALF(chan)) pll |= SM(0x1, AR_RTC_SOWL_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_QUARTER(chan)) pll |= SM(0x2, AR_RTC_SOWL_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_5GHZ(chan)) pll |= SM(0x50, AR_RTC_SOWL_PLL_DIV); else pll |= SM(0x58, AR_RTC_SOWL_PLL_DIV); } else pll |= SM(0x58, AR_RTC_SOWL_PLL_DIV); } else { pll = AR_RTC_PLL_REFDIV_5 | AR_RTC_PLL_DIV2; if (chan != AH_NULL) { if (IEEE80211_IS_CHAN_HALF(chan)) pll |= SM(0x1, AR_RTC_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_QUARTER(chan)) pll |= SM(0x2, AR_RTC_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_5GHZ(chan)) pll |= SM(0xa, AR_RTC_PLL_DIV); else pll |= SM(0xb, AR_RTC_PLL_DIV); } else pll |= SM(0xb, AR_RTC_PLL_DIV); } OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); /* TODO: * For multi-band owl, switch between bands by reiniting the PLL. */ OS_DELAY(RTC_PLL_SETTLE_DELAY); OS_REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_SLEEP_DERIVED_CLK); } /* * Read EEPROM header info and program the device for correct operation * given the channel value. */ HAL_BOOL ar5416SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) { const HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom; const struct ar5416eeprom *eep = &ee->ee_base; const MODAL_EEP_HEADER *pModal; int i, regChainOffset; uint8_t txRxAttenLocal; /* workaround for eeprom versions <= 14.2 */ HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); pModal = &eep->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)]; /* NB: workaround for eeprom versions <= 14.2 */ txRxAttenLocal = IEEE80211_IS_CHAN_2GHZ(chan) ? 23 : 44; OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, pModal->antCtrlCommon); for (i = 0; i < AR5416_MAX_CHAINS; i++) { if (AR_SREV_MERLIN(ah)) { if (i >= 2) break; } if (AR_SREV_OWL_20_OR_LATER(ah) && (AH5416(ah)->ah_rx_chainmask == 0x5 || AH5416(ah)->ah_tx_chainmask == 0x5) && i != 0) { /* Regs are swapped from chain 2 to 1 for 5416 2_0 with * only chains 0 and 2 populated */ regChainOffset = (i == 1) ? 0x2000 : 0x1000; } else { regChainOffset = i * 0x1000; } OS_REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, pModal->antCtrlChain[i]); OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4 + regChainOffset, (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4 + regChainOffset) & ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | SM(pModal->iqCalICh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | SM(pModal->iqCalQCh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); /* * Large signal upgrade. * XXX update */ if ((i == 0) || AR_SREV_OWL_20_OR_LATER(ah)) { OS_REG_WRITE(ah, AR_PHY_RXGAIN + regChainOffset, (OS_REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) & ~AR_PHY_RXGAIN_TXRX_ATTEN) | SM(IS_EEP_MINOR_V3(ah) ? pModal->txRxAttenCh[i] : txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN)); OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); } } OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->switchSettling); OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, pModal->adcDesiredSize); OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_PGA, pModal->pgaDesiredSize); OS_REG_WRITE(ah, AR_PHY_RF_CTL4, SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) | SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON)); OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); if (AR_SREV_MERLIN_10_OR_LATER(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, pModal->thresh62); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62, pModal->thresh62); } else { OS_REG_RMW_FIELD(ah, AR_PHY_CCA, AR_PHY_CCA_THRESH62, pModal->thresh62); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA_THRESH62, pModal->thresh62); } /* Minor Version Specific application */ if (IS_EEP_MINOR_V2(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_DATA_START, pModal->txFrameToDataStart); OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_PA_ON, pModal->txFrameToPaOn); } if (IS_EEP_MINOR_V3(ah)) { if (IEEE80211_IS_CHAN_HT40(chan)) { /* Overwrite switch settling with HT40 value */ OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); } if ((AR_SREV_OWL_20_OR_LATER(ah)) && ( AH5416(ah)->ah_rx_chainmask == 0x5 || AH5416(ah)->ah_tx_chainmask == 0x5)){ /* Reg Offsets are swapped for logical mapping */ OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x1000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x1000) & ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | SM(pModal->bswMargin[2], AR_PHY_GAIN_2GHZ_BSW_MARGIN)); OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x1000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x1000) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | SM(pModal->bswAtten[2], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x2000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x2000) & ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | SM(pModal->bswMargin[1], AR_PHY_GAIN_2GHZ_BSW_MARGIN)); OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x2000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x2000) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | SM(pModal->bswAtten[1], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); } else { OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x1000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x1000) & ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | SM(pModal->bswMargin[1], AR_PHY_GAIN_2GHZ_BSW_MARGIN)); OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x1000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x1000) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | SM(pModal->bswAtten[1], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x2000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x2000) & ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | SM(pModal->bswMargin[2],AR_PHY_GAIN_2GHZ_BSW_MARGIN)); OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x2000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x2000) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | SM(pModal->bswAtten[2], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); } OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_BSW_MARGIN, pModal->bswMargin[0]); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_BSW_ATTEN, pModal->bswAtten[0]); } return AH_TRUE; } /* * Helper functions common for AP/CB/XB */ /* * ar5416SetPowerPerRateTable * * Sets the transmit power in the baseband for the given * operating channel and mode. */ static HAL_BOOL ar5416SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *ratesArray, uint16_t cfgCtl, uint16_t AntennaReduction, uint16_t twiceMaxRegulatoryPower, uint16_t powerLimit) { #define N(a) (sizeof(a)/sizeof(a[0])) /* Local defines to distinguish between extension and control CTL's */ #define EXT_ADDITIVE (0x8000) #define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE) #define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) #define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) uint16_t twiceMaxEdgePower = AR5416_MAX_RATE_POWER; int i; int16_t twiceLargestAntenna; CAL_CTL_DATA *rep; CAL_TARGET_POWER_LEG targetPowerOfdm, targetPowerCck = {0, {0, 0, 0, 0}}; CAL_TARGET_POWER_LEG targetPowerOfdmExt = {0, {0, 0, 0, 0}}, targetPowerCckExt = {0, {0, 0, 0, 0}}; CAL_TARGET_POWER_HT targetPowerHt20, targetPowerHt40 = {0, {0, 0, 0, 0}}; int16_t scaledPower, minCtlPower; #define SUB_NUM_CTL_MODES_AT_5G_40 2 /* excluding HT40, EXT-OFDM */ #define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */ static const uint16_t ctlModesFor11a[] = { CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40 }; static const uint16_t ctlModesFor11g[] = { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40 }; const uint16_t *pCtlMode; uint16_t numCtlModes, ctlMode, freq; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); /* Compute TxPower reduction due to Antenna Gain */ twiceLargestAntenna = AH_MAX(AH_MAX( pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[0], pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[1]), pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[2]); #if 0 /* Turn it back on if we need to calculate per chain antenna gain reduction */ /* Use only if the expected gain > 6dbi */ /* Chain 0 is always used */ twiceLargestAntenna = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[0]; /* Look at antenna gains of Chains 1 and 2 if the TX mask is set */ if (ahp->ah_tx_chainmask & 0x2) twiceLargestAntenna = AH_MAX(twiceLargestAntenna, pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[1]); if (ahp->ah_tx_chainmask & 0x4) twiceLargestAntenna = AH_MAX(twiceLargestAntenna, pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[2]); #endif twiceLargestAntenna = (int16_t)AH_MIN((AntennaReduction) - twiceLargestAntenna, 0); /* XXX setup for 5212 use (really used?) */ ath_hal_eepromSet(ah, IEEE80211_IS_CHAN_2GHZ(chan) ? AR_EEP_ANTGAINMAX_2 : AR_EEP_ANTGAINMAX_5, twiceLargestAntenna); /* * scaledPower is the minimum of the user input power level and * the regulatory allowed power level */ scaledPower = AH_MIN(powerLimit, twiceMaxRegulatoryPower + twiceLargestAntenna); /* Reduce scaled Power by number of chains active to get to per chain tx power level */ /* TODO: better value than these? */ switch (owl_get_ntxchains(AH5416(ah)->ah_tx_chainmask)) { case 1: break; case 2: scaledPower -= pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pwrDecreaseFor2Chain; break; case 3: scaledPower -= pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pwrDecreaseFor3Chain; break; default: return AH_FALSE; /* Unsupported number of chains */ } scaledPower = AH_MAX(0, scaledPower); /* Get target powers from EEPROM - our baseline for TX Power */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { /* Setup for CTL modes */ numCtlModes = N(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40; /* CTL_11B, CTL_11G, CTL_2GHT20 */ pCtlMode = ctlModesFor11g; ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPowerCck, AR5416_NUM_2G_CCK_TARGET_POWERS, &targetPowerCck, 4, AH_FALSE); ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower2G, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerOfdm, 4, AH_FALSE); ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower2GHT20, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerHt20, 8, AH_FALSE); if (IEEE80211_IS_CHAN_HT40(chan)) { numCtlModes = N(ctlModesFor11g); /* All 2G CTL's */ ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower2GHT40, AR5416_NUM_2G_40_TARGET_POWERS, &targetPowerHt40, 8, AH_TRUE); /* Get target powers for extension channels */ ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPowerCck, AR5416_NUM_2G_CCK_TARGET_POWERS, &targetPowerCckExt, 4, AH_TRUE); ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower2G, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, AH_TRUE); } } else { /* Setup for CTL modes */ numCtlModes = N(ctlModesFor11a) - SUB_NUM_CTL_MODES_AT_5G_40; /* CTL_11A, CTL_5GHT20 */ pCtlMode = ctlModesFor11a; ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower5G, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerOfdm, 4, AH_FALSE); ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower5GHT20, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerHt20, 8, AH_FALSE); if (IEEE80211_IS_CHAN_HT40(chan)) { numCtlModes = N(ctlModesFor11a); /* All 5G CTL's */ ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower5GHT40, AR5416_NUM_5G_40_TARGET_POWERS, &targetPowerHt40, 8, AH_TRUE); ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower5G, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, AH_TRUE); } } /* * For MIMO, need to apply regulatory caps individually across dynamically * running modes: CCK, OFDM, HT20, HT40 * * The outer loop walks through each possible applicable runtime mode. * The inner loop walks through each ctlIndex entry in EEPROM. * The ctl value is encoded as [7:4] == test group, [3:0] == test mode. * */ for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { HAL_BOOL isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) || (pCtlMode[ctlMode] == CTL_2GHT40); if (isHt40CtlMode) { freq = centers.ctl_center; } else if (pCtlMode[ctlMode] & EXT_ADDITIVE) { freq = centers.ext_center; } else { freq = centers.ctl_center; } /* walk through each CTL index stored in EEPROM */ for (i = 0; (i < AR5416_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { uint16_t twiceMinEdgePower; /* compare test group from regulatory channel list with test mode from pCtlMode list */ if ((((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == pEepData->ctlIndex[i]) || (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) { rep = &(pEepData->ctlData[i]); twiceMinEdgePower = ar5416GetMaxEdgePower(freq, rep->ctlEdges[owl_get_ntxchains(AH5416(ah)->ah_tx_chainmask) - 1], IEEE80211_IS_CHAN_2GHZ(chan)); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { /* Find the minimum of all CTL edge powers that apply to this channel */ twiceMaxEdgePower = AH_MIN(twiceMaxEdgePower, twiceMinEdgePower); } else { /* specific */ twiceMaxEdgePower = twiceMinEdgePower; break; } } } minCtlPower = (uint8_t)AH_MIN(twiceMaxEdgePower, scaledPower); /* Apply ctl mode to correct target power set */ switch(pCtlMode[ctlMode]) { case CTL_11B: for (i = 0; i < N(targetPowerCck.tPow2x); i++) { targetPowerCck.tPow2x[i] = (uint8_t)AH_MIN(targetPowerCck.tPow2x[i], minCtlPower); } break; case CTL_11A: case CTL_11G: for (i = 0; i < N(targetPowerOfdm.tPow2x); i++) { targetPowerOfdm.tPow2x[i] = (uint8_t)AH_MIN(targetPowerOfdm.tPow2x[i], minCtlPower); } break; case CTL_5GHT20: case CTL_2GHT20: for (i = 0; i < N(targetPowerHt20.tPow2x); i++) { targetPowerHt20.tPow2x[i] = (uint8_t)AH_MIN(targetPowerHt20.tPow2x[i], minCtlPower); } break; case CTL_11B_EXT: targetPowerCckExt.tPow2x[0] = (uint8_t)AH_MIN(targetPowerCckExt.tPow2x[0], minCtlPower); break; case CTL_11A_EXT: case CTL_11G_EXT: targetPowerOfdmExt.tPow2x[0] = (uint8_t)AH_MIN(targetPowerOfdmExt.tPow2x[0], minCtlPower); break; case CTL_5GHT40: case CTL_2GHT40: for (i = 0; i < N(targetPowerHt40.tPow2x); i++) { targetPowerHt40.tPow2x[i] = (uint8_t)AH_MIN(targetPowerHt40.tPow2x[i], minCtlPower); } break; default: return AH_FALSE; break; } } /* end ctl mode checking */ /* Set rates Array from collected data */ ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] = ratesArray[rate18mb] = ratesArray[rate24mb] = targetPowerOfdm.tPow2x[0]; ratesArray[rate36mb] = targetPowerOfdm.tPow2x[1]; ratesArray[rate48mb] = targetPowerOfdm.tPow2x[2]; ratesArray[rate54mb] = targetPowerOfdm.tPow2x[3]; ratesArray[rateXr] = targetPowerOfdm.tPow2x[0]; for (i = 0; i < N(targetPowerHt20.tPow2x); i++) { ratesArray[rateHt20_0 + i] = targetPowerHt20.tPow2x[i]; } if (IEEE80211_IS_CHAN_2GHZ(chan)) { ratesArray[rate1l] = targetPowerCck.tPow2x[0]; ratesArray[rate2s] = ratesArray[rate2l] = targetPowerCck.tPow2x[1]; ratesArray[rate5_5s] = ratesArray[rate5_5l] = targetPowerCck.tPow2x[2]; ratesArray[rate11s] = ratesArray[rate11l] = targetPowerCck.tPow2x[3]; } if (IEEE80211_IS_CHAN_HT40(chan)) { for (i = 0; i < N(targetPowerHt40.tPow2x); i++) { ratesArray[rateHt40_0 + i] = targetPowerHt40.tPow2x[i]; } ratesArray[rateDupOfdm] = targetPowerHt40.tPow2x[0]; ratesArray[rateDupCck] = targetPowerHt40.tPow2x[0]; ratesArray[rateExtOfdm] = targetPowerOfdmExt.tPow2x[0]; if (IEEE80211_IS_CHAN_2GHZ(chan)) { ratesArray[rateExtCck] = targetPowerCckExt.tPow2x[0]; } } return AH_TRUE; #undef EXT_ADDITIVE #undef CTL_11A_EXT #undef CTL_11G_EXT #undef CTL_11B_EXT #undef SUB_NUM_CTL_MODES_AT_5G_40 #undef SUB_NUM_CTL_MODES_AT_2G_40 #undef N } /************************************************************************** * fbin2freq * * Get channel value from binary representation held in eeprom * RETURNS: the frequency in MHz */ static uint16_t fbin2freq(uint8_t fbin, HAL_BOOL is2GHz) { /* * Reserved value 0xFF provides an empty definition both as * an fbin and as a frequency - do not convert */ if (fbin == AR5416_BCHAN_UNUSED) { return fbin; } return (uint16_t)((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); } /* * ar5416GetMaxEdgePower * * Find the maximum conformance test limit for the given channel and CTL info */ static uint16_t ar5416GetMaxEdgePower(uint16_t freq, CAL_CTL_EDGES *pRdEdgesPower, HAL_BOOL is2GHz) { uint16_t twiceMaxEdgePower = AR5416_MAX_RATE_POWER; int i; /* Get the edge power */ for (i = 0; (i < AR5416_NUM_BAND_EDGES) && (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED) ; i++) { /* * If there's an exact channel match or an inband flag set * on the lower channel use the given rdEdgePower */ if (freq == fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) { twiceMaxEdgePower = MS(pRdEdgesPower[i].tPowerFlag, CAL_CTL_EDGES_POWER); break; } else if ((i > 0) && (freq < fbin2freq(pRdEdgesPower[i].bChannel, is2GHz))) { if (fbin2freq(pRdEdgesPower[i - 1].bChannel, is2GHz) < freq && (pRdEdgesPower[i - 1].tPowerFlag & CAL_CTL_EDGES_FLAG) != 0) { twiceMaxEdgePower = MS(pRdEdgesPower[i - 1].tPowerFlag, CAL_CTL_EDGES_POWER); } /* Leave loop - no more affecting edges possible in this monotonic increasing list */ break; } } HALASSERT(twiceMaxEdgePower > 0); return twiceMaxEdgePower; } /************************************************************** * ar5416GetTargetPowers * * Return the rates of target power for the given target power table * channel, and number of channels */ void ar5416GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_TARGET_POWER_HT *powInfo, uint16_t numChannels, CAL_TARGET_POWER_HT *pNewPower, uint16_t numRates, HAL_BOOL isHt40Target) { uint16_t clo, chi; int i; int matchIndex = -1, lowIndex = -1; uint16_t freq; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); freq = isHt40Target ? centers.synth_center : centers.ctl_center; /* Copy the target powers into the temp channel list */ if (freq <= fbin2freq(powInfo[0].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = 0; } else { for (i = 0; (i < numChannels) && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { if (freq == fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = i; break; } else if ((freq < fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) && (freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)))) { lowIndex = i - 1; break; } } if ((matchIndex == -1) && (lowIndex == -1)) { HALASSERT(freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))); matchIndex = i - 1; } } if (matchIndex != -1) { OS_MEMCPY(pNewPower, &powInfo[matchIndex], sizeof(*pNewPower)); } else { HALASSERT(lowIndex != -1); /* * Get the lower and upper channels, target powers, * and interpolate between them. */ clo = fbin2freq(powInfo[lowIndex].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); chi = fbin2freq(powInfo[lowIndex + 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); for (i = 0; i < numRates; i++) { pNewPower->tPow2x[i] = (uint8_t)interpolate(freq, clo, chi, powInfo[lowIndex].tPow2x[i], powInfo[lowIndex + 1].tPow2x[i]); } } } /************************************************************** * ar5416GetTargetPowersLeg * * Return the four rates of target power for the given target power table * channel, and number of channels */ void ar5416GetTargetPowersLeg(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_TARGET_POWER_LEG *powInfo, uint16_t numChannels, CAL_TARGET_POWER_LEG *pNewPower, uint16_t numRates, HAL_BOOL isExtTarget) { uint16_t clo, chi; int i; int matchIndex = -1, lowIndex = -1; uint16_t freq; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); freq = (isExtTarget) ? centers.ext_center :centers.ctl_center; /* Copy the target powers into the temp channel list */ if (freq <= fbin2freq(powInfo[0].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = 0; } else { for (i = 0; (i < numChannels) && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { if (freq == fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = i; break; } else if ((freq < fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) && (freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)))) { lowIndex = i - 1; break; } } if ((matchIndex == -1) && (lowIndex == -1)) { HALASSERT(freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))); matchIndex = i - 1; } } if (matchIndex != -1) { OS_MEMCPY(pNewPower, &powInfo[matchIndex], sizeof(*pNewPower)); } else { HALASSERT(lowIndex != -1); /* * Get the lower and upper channels, target powers, * and interpolate between them. */ clo = fbin2freq(powInfo[lowIndex].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); chi = fbin2freq(powInfo[lowIndex + 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); for (i = 0; i < numRates; i++) { pNewPower->tPow2x[i] = (uint8_t)interpolate(freq, clo, chi, powInfo[lowIndex].tPow2x[i], powInfo[lowIndex + 1].tPow2x[i]); } } } /************************************************************** * ar5416SetPowerCalTable * * Pull the PDADC piers from cal data and interpolate them across the given * points as well as from the nearest pier(s) to get a power detector * linear voltage to power level table. */ static HAL_BOOL ar5416SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) { CAL_DATA_PER_FREQ *pRawDataset; uint8_t *pCalBChans = AH_NULL; uint16_t pdGainOverlap_t2; static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]; uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK]; uint16_t numPiers, i, j; int16_t tMinCalPower; uint16_t numXpdGain, xpdMask; uint16_t xpdGainValues[AR5416_NUM_PD_GAINS]; uint32_t reg32, regOffset, regChainOffset; OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain; if (IS_EEP_MINOR_V2(ah)) { pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap; } else { pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); } if (IEEE80211_IS_CHAN_2GHZ(chan)) { pCalBChans = pEepData->calFreqPier2G; numPiers = AR5416_NUM_2G_CAL_PIERS; } else { pCalBChans = pEepData->calFreqPier5G; numPiers = AR5416_NUM_5G_CAL_PIERS; } numXpdGain = 0; /* Calculate the value of xpdgains from the xpdGain Mask */ for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { if (numXpdGain >= AR5416_NUM_PD_GAINS) { HALASSERT(0); break; } xpdGainValues[numXpdGain] = (uint16_t)(AR5416_PD_GAINS_IN_MASK - i); numXpdGain++; } } /* Write the detector gain biases and their number */ OS_REG_WRITE(ah, AR_PHY_TPCRG1, (OS_REG_READ(ah, AR_PHY_TPCRG1) & ~(AR_PHY_TPCRG1_NUM_PD_GAIN | AR_PHY_TPCRG1_PD_GAIN_1 | AR_PHY_TPCRG1_PD_GAIN_2 | AR_PHY_TPCRG1_PD_GAIN_3)) | SM(numXpdGain - 1, AR_PHY_TPCRG1_NUM_PD_GAIN) | SM(xpdGainValues[0], AR_PHY_TPCRG1_PD_GAIN_1 ) | SM(xpdGainValues[1], AR_PHY_TPCRG1_PD_GAIN_2) | SM(xpdGainValues[2], AR_PHY_TPCRG1_PD_GAIN_3)); for (i = 0; i < AR5416_MAX_CHAINS; i++) { if (AR_SREV_OWL_20_OR_LATER(ah) && ( AH5416(ah)->ah_rx_chainmask == 0x5 || AH5416(ah)->ah_tx_chainmask == 0x5) && (i != 0)) { /* Regs are swapped from chain 2 to 1 for 5416 2_0 with * only chains 0 and 2 populated */ regChainOffset = (i == 1) ? 0x2000 : 0x1000; } else { regChainOffset = i * 0x1000; } if (pEepData->baseEepHeader.txMask & (1 << i)) { if (IEEE80211_IS_CHAN_2GHZ(chan)) { pRawDataset = pEepData->calPierData2G[i]; } else { pRawDataset = pEepData->calPierData5G[i]; } ar5416GetGainBoundariesAndPdadcs(ah, chan, pRawDataset, pCalBChans, numPiers, pdGainOverlap_t2, &tMinCalPower, gainBoundaries, pdadcValues, numXpdGain); if ((i == 0) || AR_SREV_OWL_20_OR_LATER(ah)) { /* * Note the pdadc table may not start at 0 dBm power, could be * negative or greater than 0. Need to offset the power * values by the amount of minPower for griffin */ OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); } /* Write the power values into the baseband power table */ regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset; for (j = 0; j < 32; j++) { reg32 = ((pdadcValues[4*j + 0] & 0xFF) << 0) | ((pdadcValues[4*j + 1] & 0xFF) << 8) | ((pdadcValues[4*j + 2] & 0xFF) << 16) | ((pdadcValues[4*j + 3] & 0xFF) << 24) ; OS_REG_WRITE(ah, regOffset, reg32); #ifdef PDADC_DUMP ath_hal_printf(ah, "PDADC: Chain %d | PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d Value %3d |\n", i, 4*j, pdadcValues[4*j], 4*j+1, pdadcValues[4*j + 1], 4*j+2, pdadcValues[4*j + 2], 4*j+3, pdadcValues[4*j + 3]); #endif regOffset += 4; } } } *pTxPowerIndexOffset = 0; return AH_TRUE; } /************************************************************** * ar5416GetGainBoundariesAndPdadcs * * Uses the data points read from EEPROM to reconstruct the pdadc power table * Called by ar5416SetPowerCalTable only. */ static void ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ *pRawDataSet, uint8_t * bChans, uint16_t availPiers, uint16_t tPdGainOverlap, int16_t *pMinCalPower, uint16_t * pPdGainBoundaries, uint8_t * pPDADCValues, uint16_t numXpdGains) { int i, j, k; int16_t ss; /* potentially -ve index for taking care of pdGainOverlap */ uint16_t idxL, idxR, numPiers; /* Pier indexes */ /* filled out Vpd table for all pdGains (chanL) */ static uint8_t vpdTableL[AR5416_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (chanR) */ static uint8_t vpdTableR[AR5416_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (interpolated) */ static uint8_t vpdTableI[AR5416_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; uint8_t *pVpdL, *pVpdR, *pPwrL, *pPwrR; uint8_t minPwrT4[AR5416_NUM_PD_GAINS]; uint8_t maxPwrT4[AR5416_NUM_PD_GAINS]; int16_t vpdStep; int16_t tmpVal; uint16_t sizeCurrVpdTable, maxIndex, tgtIndex; HAL_BOOL match; int16_t minDelta = 0; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); /* Trim numPiers for the number of populated channel Piers */ for (numPiers = 0; numPiers < availPiers; numPiers++) { if (bChans[numPiers] == AR5416_BCHAN_UNUSED) { break; } } /* Find pier indexes around the current channel */ match = getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)), bChans, numPiers, &idxL, &idxR); if (match) { /* Directly fill both vpd tables from the matching index */ for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][0]; maxPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][4]; ar5416FillVpdTable(minPwrT4[i], maxPwrT4[i], pRawDataSet[idxL].pwrPdg[i], pRawDataSet[idxL].vpdPdg[i], AR5416_PD_GAIN_ICEPTS, vpdTableI[i]); } } else { for (i = 0; i < numXpdGains; i++) { pVpdL = pRawDataSet[idxL].vpdPdg[i]; pPwrL = pRawDataSet[idxL].pwrPdg[i]; pVpdR = pRawDataSet[idxR].vpdPdg[i]; pPwrR = pRawDataSet[idxR].pwrPdg[i]; /* Start Vpd interpolation from the max of the minimum powers */ minPwrT4[i] = AH_MAX(pPwrL[0], pPwrR[0]); /* End Vpd interpolation from the min of the max powers */ maxPwrT4[i] = AH_MIN(pPwrL[AR5416_PD_GAIN_ICEPTS - 1], pPwrR[AR5416_PD_GAIN_ICEPTS - 1]); HALASSERT(maxPwrT4[i] > minPwrT4[i]); /* Fill pier Vpds */ ar5416FillVpdTable(minPwrT4[i], maxPwrT4[i], pPwrL, pVpdL, AR5416_PD_GAIN_ICEPTS, vpdTableL[i]); ar5416FillVpdTable(minPwrT4[i], maxPwrT4[i], pPwrR, pVpdR, AR5416_PD_GAIN_ICEPTS, vpdTableR[i]); /* Interpolate the final vpd */ for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) { vpdTableI[i][j] = (uint8_t)(interpolate((uint16_t)FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)), bChans[idxL], bChans[idxR], vpdTableL[i][j], vpdTableR[i][j])); } } } *pMinCalPower = (int16_t)(minPwrT4[0] / 2); k = 0; /* index for the final table */ for (i = 0; i < numXpdGains; i++) { if (i == (numXpdGains - 1)) { pPdGainBoundaries[i] = (uint16_t)(maxPwrT4[i] / 2); } else { pPdGainBoundaries[i] = (uint16_t)((maxPwrT4[i] + minPwrT4[i+1]) / 4); } pPdGainBoundaries[i] = (uint16_t)AH_MIN(AR5416_MAX_RATE_POWER, pPdGainBoundaries[i]); /* NB: only applies to owl 1.0 */ if ((i == 0) && !AR_SREV_OWL_20_OR_LATER(ah) ) { /* * fix the gain delta, but get a delta that can be applied to min to * keep the upper power values accurate, don't think max needs to * be adjusted because should not be at that area of the table? */ minDelta = pPdGainBoundaries[0] - 23; pPdGainBoundaries[0] = 23; } else { minDelta = 0; } /* Find starting index for this pdGain */ if (i == 0) { ss = 0; /* for the first pdGain, start from index 0 */ } else { /* need overlap entries extrapolated below. */ ss = (int16_t)((pPdGainBoundaries[i-1] - (minPwrT4[i] / 2)) - tPdGainOverlap + 1 + minDelta); } vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]); vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); /* *-ve ss indicates need to extrapolate data below for this pdGain */ while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep); pPDADCValues[k++] = (uint8_t)((tmpVal < 0) ? 0 : tmpVal); ss++; } sizeCurrVpdTable = (uint8_t)((maxPwrT4[i] - minPwrT4[i]) / 2 +1); tgtIndex = (uint8_t)(pPdGainBoundaries[i] + tPdGainOverlap - (minPwrT4[i] / 2)); maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { pPDADCValues[k++] = vpdTableI[i][ss++]; } vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] - vpdTableI[i][sizeCurrVpdTable - 2]); vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); /* * for last gain, pdGainBoundary == Pmax_t2, so will * have to extrapolate */ if (tgtIndex > maxIndex) { /* need to extrapolate above */ while ((ss <= tgtIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] + (ss - maxIndex +1) * vpdStep)); pPDADCValues[k++] = (uint8_t)((tmpVal > 255) ? 255 : tmpVal); ss++; } } /* extrapolated above */ } /* for all pdGainUsed */ /* Fill out pdGainBoundaries - only up to 2 allowed here, but hardware allows up to 4 */ while (i < AR5416_PD_GAINS_IN_MASK) { pPdGainBoundaries[i] = pPdGainBoundaries[i-1]; i++; } while (k < AR5416_NUM_PDADC_VALUES) { pPDADCValues[k] = pPDADCValues[k-1]; k++; } return; } /************************************************************** * getLowerUppderIndex * * Return indices surrounding the value in sorted integer lists. * Requirement: the input list must be monotonically increasing * and populated up to the list size * Returns: match is set if an index in the array matches exactly * or a the target is before or after the range of the array. */ HAL_BOOL getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize, uint16_t *indexL, uint16_t *indexR) { uint16_t i; /* * Check first and last elements for beyond ordered array cases. */ if (target <= pList[0]) { *indexL = *indexR = 0; return AH_TRUE; } if (target >= pList[listSize-1]) { *indexL = *indexR = (uint16_t)(listSize - 1); return AH_TRUE; } /* look for value being near or between 2 values in list */ for (i = 0; i < listSize - 1; i++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (pList[i] == target) { *indexL = *indexR = i; return AH_TRUE; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < pList[i + 1]) { *indexL = i; *indexR = (uint16_t)(i + 1); return AH_FALSE; } } HALASSERT(0); *indexL = *indexR = 0; return AH_FALSE; } /************************************************************** * ar5416FillVpdTable * * Fill the Vpdlist for indices Pmax-Pmin * Note: pwrMin, pwrMax and Vpdlist are all in dBm * 4 */ static HAL_BOOL ar5416FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList, uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList) { uint16_t i, k; uint8_t currPwr = pwrMin; uint16_t idxL, idxR; HALASSERT(pwrMax > pwrMin); for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { getLowerUpperIndex(currPwr, pPwrList, numIntercepts, &(idxL), &(idxR)); if (idxR < 1) idxR = 1; /* extrapolate below */ if (idxL == numIntercepts - 1) idxL = (uint16_t)(numIntercepts - 2); /* extrapolate above */ if (pPwrList[idxL] == pPwrList[idxR]) k = pVpdList[idxL]; else k = (uint16_t)( ((currPwr - pPwrList[idxL]) * pVpdList[idxR] + (pPwrList[idxR] - currPwr) * pVpdList[idxL]) / (pPwrList[idxR] - pPwrList[idxL]) ); HALASSERT(k < 256); pRetVpdList[i] = (uint8_t)k; currPwr += 2; /* half dB steps */ } return AH_TRUE; } /************************************************************************** * interpolate * * Returns signed interpolated or the scaled up interpolated value */ static int16_t interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight) { int16_t rv; if (srcRight == srcLeft) { rv = targetLeft; } else { rv = (int16_t)( ((target - srcLeft) * targetRight + (srcRight - target) * targetLeft) / (srcRight - srcLeft) ); } return rv; } static void ar5416Set11nRegs(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t phymode; HAL_HT_MACMODE macmode; /* MAC - 20/40 mode */ if (!IEEE80211_IS_CHAN_HT(chan)) return; /* Enable 11n HT, 20 MHz */ phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40 | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH; /* Configure baseband for dynamic 20/40 operation */ if (IEEE80211_IS_CHAN_HT40(chan)) { phymode |= AR_PHY_FC_DYN2040_EN | AR_PHY_FC_SHORT_GI_40; /* Configure control (primary) channel at +-10MHz */ if (IEEE80211_IS_CHAN_HT40U(chan)) phymode |= AR_PHY_FC_DYN2040_PRI_CH; #if 0 /* Configure 20/25 spacing */ if (ht->ht_extprotspacing == HAL_HT_EXTPROTSPACING_25) phymode |= AR_PHY_FC_DYN2040_EXT_CH; #endif macmode = HAL_HT_MACMODE_2040; } else macmode = HAL_HT_MACMODE_20; OS_REG_WRITE(ah, AR_PHY_TURBO, phymode); /* Configure MAC for 20/40 operation */ ar5416Set11nMac2040(ah, macmode); /* global transmit timeout (25 TUs default)*/ /* XXX - put this elsewhere??? */ OS_REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S) ; /* carrier sense timeout */ OS_REG_SET_BIT(ah, AR_GTTM, AR_GTTM_CST_USEC); OS_REG_WRITE(ah, AR_CST, 1 << AR_CST_TIMEOUT_LIMIT_S); } void ar5416GetChannelCenters(struct ath_hal *ah, const struct ieee80211_channel *chan, CHAN_CENTERS *centers) { uint16_t freq = ath_hal_gethwchannel(ah, chan); centers->ctl_center = freq; centers->synth_center = freq; /* * In 20/40 phy mode, the center frequency is * "between" the control and extension channels. */ if (IEEE80211_IS_CHAN_HT40U(chan)) { centers->synth_center += HT40_CHANNEL_CENTER_SHIFT; centers->ext_center = centers->synth_center + HT40_CHANNEL_CENTER_SHIFT; } else if (IEEE80211_IS_CHAN_HT40D(chan)) { centers->synth_center -= HT40_CHANNEL_CENTER_SHIFT; centers->ext_center = centers->synth_center - HT40_CHANNEL_CENTER_SHIFT; } else { centers->ext_center = freq; } } diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c b/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c index 47277a18b4ed..98059350309c 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c @@ -1,695 +1,696 @@ /* * 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_desc.h" #include "ah_internal.h" #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar5416/ar5416desc.h" /* * Stop transmit on the specified queue */ HAL_BOOL ar5416StopTxDma(struct ath_hal *ah, u_int q) { #define STOP_DMA_TIMEOUT 4000 /* us */ #define STOP_DMA_ITER 100 /* us */ u_int i; HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues); HALASSERT(AH5212(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE); OS_REG_WRITE(ah, AR_Q_TXD, 1 << q); for (i = STOP_DMA_TIMEOUT/STOP_DMA_ITER; i != 0; i--) { if (ar5212NumTxPending(ah, q) == 0) break; OS_DELAY(STOP_DMA_ITER); } #ifdef AH_DEBUG if (i == 0) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: queue %u DMA did not stop in 400 msec\n", __func__, q); HALDEBUG(ah, HAL_DEBUG_ANY, "%s: QSTS 0x%x Q_TXE 0x%x Q_TXD 0x%x Q_CBR 0x%x\n", __func__, OS_REG_READ(ah, AR_QSTS(q)), OS_REG_READ(ah, AR_Q_TXE), OS_REG_READ(ah, AR_Q_TXD), OS_REG_READ(ah, AR_QCBRCFG(q))); HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Q_MISC 0x%x Q_RDYTIMECFG 0x%x Q_RDYTIMESHDN 0x%x\n", __func__, OS_REG_READ(ah, AR_QMISC(q)), OS_REG_READ(ah, AR_QRDYTIMECFG(q)), OS_REG_READ(ah, AR_Q_RDYTIMESHDN)); } #endif /* AH_DEBUG */ /* ar5416 and up can kill packets at the PCU level */ if (ar5212NumTxPending(ah, q)) { uint32_t j; HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: Num of pending TX Frames %d on Q %d\n", __func__, ar5212NumTxPending(ah, q), q); /* Kill last PCU Tx Frame */ /* TODO - save off and restore current values of Q1/Q2? */ for (j = 0; j < 2; j++) { uint32_t tsfLow = OS_REG_READ(ah, AR_TSF_L32); OS_REG_WRITE(ah, AR_QUIET2, SM(10, AR_QUIET2_QUIET_DUR)); OS_REG_WRITE(ah, AR_QUIET_PERIOD, 100); OS_REG_WRITE(ah, AR_NEXT_QUIET, tsfLow >> 10); OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_QUIET); if ((OS_REG_READ(ah, AR_TSF_L32)>>10) == (tsfLow>>10)) break; HALDEBUG(ah, HAL_DEBUG_ANY, "%s: TSF moved while trying to set quiet time " "TSF: 0x%08x\n", __func__, tsfLow); HALASSERT(j < 1); /* TSF shouldn't count twice or reg access is taking forever */ } OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_CHAN_IDLE); /* Allow the quiet mechanism to do its work */ OS_DELAY(200); OS_REG_CLR_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_QUIET); /* Verify the transmit q is empty */ for (i = STOP_DMA_TIMEOUT/STOP_DMA_ITER; i != 0; i--) { if (ar5212NumTxPending(ah, q) == 0) break; OS_DELAY(STOP_DMA_ITER); } if (i == 0) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Failed to stop Tx DMA in %d msec after killing" " last frame\n", __func__, STOP_DMA_TIMEOUT / 1000); } OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_CHAN_IDLE); } OS_REG_WRITE(ah, AR_Q_TXD, 0); return (i != 0); #undef STOP_DMA_ITER #undef STOP_DMA_TIMEOUT } #define VALID_KEY_TYPES \ ((1 << HAL_KEY_TYPE_CLEAR) | (1 << HAL_KEY_TYPE_WEP)|\ (1 << HAL_KEY_TYPE_AES) | (1 << HAL_KEY_TYPE_TKIP)) #define isValidKeyType(_t) ((1 << (_t)) & VALID_KEY_TYPES) #define set11nTries(_series, _index) \ (SM((_series)[_index].Tries, AR_XmitDataTries##_index)) #define set11nRate(_series, _index) \ (SM((_series)[_index].Rate, AR_XmitRate##_index)) #define set11nPktDurRTSCTS(_series, _index) \ (SM((_series)[_index].PktDuration, AR_PacketDur##_index) |\ ((_series)[_index].RateFlags & HAL_RATESERIES_RTS_CTS ?\ AR_RTSCTSQual##_index : 0)) #define set11nRateFlags(_series, _index) \ ((_series)[_index].RateFlags & HAL_RATESERIES_2040 ? AR_2040_##_index : 0) \ |((_series)[_index].RateFlags & HAL_RATESERIES_HALFGI ? AR_GI##_index : 0) \ |SM((_series)[_index].ChSel, AR_ChainSel##_index) /* * Descriptor Access Functions */ #define VALID_PKT_TYPES \ ((1<ah_txPowerIndexOffset); if (txPower > 63) txPower = 63; ads->ds_ctl0 = (pktLen & AR_FrameLen) | (txPower << AR_XmitPower_S) | (flags & HAL_TXDESC_VEOL ? AR_VEOL : 0) | (flags & HAL_TXDESC_CLRDMASK ? AR_ClrDestMask : 0) | (flags & HAL_TXDESC_INTREQ ? AR_TxIntrReq : 0) ; ads->ds_ctl1 = (type << AR_FrameType_S) | (flags & HAL_TXDESC_NOACK ? AR_NoAck : 0) ; ads->ds_ctl2 = SM(txTries0, AR_XmitDataTries0) | (flags & HAL_TXDESC_DURENA ? AR_DurUpdateEn : 0) ; ads->ds_ctl3 = (txRate0 << AR_XmitRate0_S) ; ads->ds_ctl4 = 0; ads->ds_ctl5 = 0; ads->ds_ctl6 = 0; ads->ds_ctl7 = SM(ahp->ah_tx_chainmask, AR_ChainSel0) | SM(ahp->ah_tx_chainmask, AR_ChainSel1) | SM(ahp->ah_tx_chainmask, AR_ChainSel2) | SM(ahp->ah_tx_chainmask, AR_ChainSel3) ; ads->ds_ctl8 = 0; ads->ds_ctl9 = (txPower << 24); /* XXX? */ ads->ds_ctl10 = (txPower << 24); /* XXX? */ ads->ds_ctl11 = (txPower << 24); /* XXX? */ if (keyIx != HAL_TXKEYIX_INVALID) { /* XXX validate key index */ ads->ds_ctl1 |= SM(keyIx, AR_DestIdx); ads->ds_ctl0 |= AR_DestIdxValid; ads->ds_ctl6 |= SM(ahp->ah_keytype[keyIx], AR_EncrType); } if (flags & RTSCTS) { if (!isValidTxRate(rtsctsRate)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid rts/cts rate 0x%x\n", __func__, rtsctsRate); return AH_FALSE; } /* XXX validate rtsctsDuration */ ads->ds_ctl0 |= (flags & HAL_TXDESC_CTSENA ? AR_CTSEnable : 0) | (flags & HAL_TXDESC_RTSENA ? AR_RTSEnable : 0) ; ads->ds_ctl2 |= SM(rtsctsDuration, AR_BurstDur); ads->ds_ctl7 |= (rtsctsRate << AR_RTSCTSRate_S); } return AH_TRUE; #undef RTSCTS } HAL_BOOL ar5416SetupXTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int txRate1, u_int txTries1, u_int txRate2, u_int txTries2, u_int txRate3, u_int txTries3) { struct ar5416_desc *ads = AR5416DESC(ds); if (txTries1) { HALASSERT(isValidTxRate(txRate1)); ads->ds_ctl2 |= SM(txTries1, AR_XmitDataTries1); ads->ds_ctl3 |= (txRate1 << AR_XmitRate1_S); } if (txTries2) { HALASSERT(isValidTxRate(txRate2)); ads->ds_ctl2 |= SM(txTries2, AR_XmitDataTries2); ads->ds_ctl3 |= (txRate2 << AR_XmitRate2_S); } if (txTries3) { HALASSERT(isValidTxRate(txRate3)); ads->ds_ctl2 |= SM(txTries3, AR_XmitDataTries3); ads->ds_ctl3 |= (txRate3 << AR_XmitRate3_S); } return AH_TRUE; } HAL_BOOL ar5416FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0) { struct ar5416_desc *ads = AR5416DESC(ds); HALASSERT((segLen &~ AR_BufLen) == 0); if (firstSeg) { /* * First descriptor, don't clobber xmit control data * setup by ar5212SetupTxDesc. */ ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore); } else if (lastSeg) { /* !firstSeg && lastSeg */ /* * Last descriptor in a multi-descriptor frame, * copy the multi-rate transmit parameters from * the first frame for processing on completion. */ ads->ds_ctl0 = 0; ads->ds_ctl1 = segLen; #ifdef AH_NEED_DESC_SWAP ads->ds_ctl2 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl2); ads->ds_ctl3 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl3); #else ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2; ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3; #endif } else { /* !firstSeg && !lastSeg */ /* * Intermediate descriptor in a multi-descriptor frame. */ ads->ds_ctl0 = 0; ads->ds_ctl1 = segLen | AR_TxMore; ads->ds_ctl2 = 0; ads->ds_ctl3 = 0; } /* XXX only on last descriptor? */ OS_MEMZERO(ads->u.tx.status, sizeof(ads->u.tx.status)); return AH_TRUE; } #if 0 HAL_BOOL ar5416ChainTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int keyIx, HAL_CIPHER cipher, uint8_t delims, u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t *ds_txstatus = AR5416_DS_TXSTATUS(ah,ads); int isaggr = 0; (void) hdrLen; (void) ah; HALASSERT((segLen &~ AR_BufLen) == 0); HALASSERT(isValidPktType(type)); if (type == HAL_PKT_TYPE_AMPDU) { type = HAL_PKT_TYPE_NORMAL; isaggr = 1; } if (!firstSeg) { ath_hal_memzero(ds->ds_hw, AR5416_DESC_TX_CTL_SZ); } ads->ds_ctl0 = (pktLen & AR_FrameLen); ads->ds_ctl1 = (type << AR_FrameType_S) | (isaggr ? (AR_IsAggr | AR_MoreAggr) : 0); ads->ds_ctl2 = 0; ads->ds_ctl3 = 0; if (keyIx != HAL_TXKEYIX_INVALID) { /* XXX validate key index */ ads->ds_ctl1 |= SM(keyIx, AR_DestIdx); ads->ds_ctl0 |= AR_DestIdxValid; } ads->ds_ctl6 = SM(keyType[cipher], AR_EncrType); if (isaggr) { ads->ds_ctl6 |= SM(delims, AR_PadDelim); } if (firstSeg) { ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore); } else if (lastSeg) { /* !firstSeg && lastSeg */ ads->ds_ctl0 = 0; ads->ds_ctl1 |= segLen; } else { /* !firstSeg && !lastSeg */ /* * Intermediate descriptor in a multi-descriptor frame. */ ads->ds_ctl0 = 0; ads->ds_ctl1 |= segLen | AR_TxMore; } ds_txstatus[0] = ds_txstatus[1] = 0; ds_txstatus[9] &= ~AR_TxDone; return AH_TRUE; } HAL_BOOL ar5416SetupFirstTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen, u_int flags, u_int txPower, u_int txRate0, u_int txTries0, u_int antMode, u_int rtsctsRate, u_int rtsctsDuration) { #define RTSCTS (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA) struct ar5416_desc *ads = AR5416DESC(ds); struct ath_hal_5212 *ahp = AH5212(ah); HALASSERT(txTries0 != 0); HALASSERT(isValidTxRate(txRate0)); HALASSERT((flags & RTSCTS) != RTSCTS); /* XXX validate antMode */ txPower = (txPower + ahp->ah_txPowerIndexOffset ); if(txPower > 63) txPower=63; ads->ds_ctl0 |= (txPower << AR_XmitPower_S) | (flags & HAL_TXDESC_VEOL ? AR_VEOL : 0) | (flags & HAL_TXDESC_CLRDMASK ? AR_ClrDestMask : 0) | (flags & HAL_TXDESC_INTREQ ? AR_TxIntrReq : 0); ads->ds_ctl1 |= (flags & HAL_TXDESC_NOACK ? AR_NoAck : 0); ads->ds_ctl2 |= SM(txTries0, AR_XmitDataTries0); ads->ds_ctl3 |= (txRate0 << AR_XmitRate0_S); ads->ds_ctl7 = SM(AH5416(ah)->ah_tx_chainmask, AR_ChainSel0) | SM(AH5416(ah)->ah_tx_chainmask, AR_ChainSel1) | SM(AH5416(ah)->ah_tx_chainmask, AR_ChainSel2) | SM(AH5416(ah)->ah_tx_chainmask, AR_ChainSel3); /* NB: no V1 WAR */ ads->ds_ctl8 = 0; ads->ds_ctl9 = (txPower << 24); ads->ds_ctl10 = (txPower << 24); ads->ds_ctl11 = (txPower << 24); ads->ds_ctl6 &= ~(0xffff); ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen); if (flags & RTSCTS) { /* XXX validate rtsctsDuration */ ads->ds_ctl0 |= (flags & HAL_TXDESC_CTSENA ? AR_CTSEnable : 0) | (flags & HAL_TXDESC_RTSENA ? AR_RTSEnable : 0); ads->ds_ctl2 |= SM(rtsctsDuration, AR_BurstDur); } return AH_TRUE; #undef RTSCTS } HAL_BOOL ar5416SetupLastTxDesc(struct ath_hal *ah, struct ath_desc *ds, const struct ath_desc *ds0) { struct ar5416_desc *ads = AR5416DESC(ds); ads->ds_ctl1 &= ~AR_MoreAggr; ads->ds_ctl6 &= ~AR_PadDelim; /* hack to copy rate info to last desc for later processing */ #ifdef AH_NEED_DESC_SWAP ads->ds_ctl2 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl2); ads->ds_ctl3 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl3); #else ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2; ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3; #endif return AH_TRUE; } #endif /* 0 */ #ifdef AH_NEED_DESC_SWAP /* Swap transmit descriptor */ static __inline void ar5416SwapTxDesc(struct ath_desc *ds) { ds->ds_data = __bswap32(ds->ds_data); ds->ds_ctl0 = __bswap32(ds->ds_ctl0); ds->ds_ctl1 = __bswap32(ds->ds_ctl1); ds->ds_hw[0] = __bswap32(ds->ds_hw[0]); ds->ds_hw[1] = __bswap32(ds->ds_hw[1]); ds->ds_hw[2] = __bswap32(ds->ds_hw[2]); ds->ds_hw[3] = __bswap32(ds->ds_hw[3]); } #endif /* * Processing of HW TX descriptor. */ HAL_STATUS ar5416ProcTxDesc(struct ath_hal *ah, struct ath_desc *ds, struct ath_tx_status *ts) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t *ds_txstatus = AR5416_DS_TXSTATUS(ah,ads); #ifdef AH_NEED_DESC_SWAP if ((ds_txstatus[9] & __bswap32(AR_TxDone)) == 0) return HAL_EINPROGRESS; ar5416SwapTxDesc(ds); #else if ((ds_txstatus[9] & AR_TxDone) == 0) return HAL_EINPROGRESS; #endif /* Update software copies of the HW status */ ts->ts_seqnum = MS(ds_txstatus[9], AR_SeqNum); ts->ts_tstamp = AR_SendTimestamp(ds_txstatus); ts->ts_status = 0; if (ds_txstatus[1] & AR_ExcessiveRetries) ts->ts_status |= HAL_TXERR_XRETRY; if (ds_txstatus[1] & AR_Filtered) ts->ts_status |= HAL_TXERR_FILT; if (ds_txstatus[1] & AR_FIFOUnderrun) ts->ts_status |= HAL_TXERR_FIFO; if (ds_txstatus[9] & AR_TxOpExceeded) ts->ts_status |= HAL_TXERR_XTXOP; if (ds_txstatus[1] & AR_TxTimerExpired) ts->ts_status |= HAL_TXERR_TIMER_EXPIRED; ts->ts_flags = 0; if (ds_txstatus[0] & AR_TxBaStatus) { ts->ts_flags |= HAL_TX_BA; ts->ts_ba_low = AR_BaBitmapLow(ds_txstatus); ts->ts_ba_high = AR_BaBitmapHigh(ds_txstatus); } if (ds->ds_ctl1 & AR_IsAggr) ts->ts_flags |= HAL_TX_AGGR; if (ds_txstatus[1] & AR_DescCfgErr) ts->ts_flags |= HAL_TX_DESC_CFG_ERR; if (ds_txstatus[1] & AR_TxDataUnderrun) ts->ts_flags |= HAL_TX_DATA_UNDERRUN; if (ds_txstatus[1] & AR_TxDelimUnderrun) ts->ts_flags |= HAL_TX_DELIM_UNDERRUN; /* * Extract the transmit rate used and mark the rate as * ``alternate'' if it wasn't the series 0 rate. */ ts->ts_finaltsi = MS(ds_txstatus[9], AR_FinalTxIdx); switch (ts->ts_finaltsi) { case 0: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate0); break; case 1: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate1); break; case 2: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate2); break; case 3: ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate3); break; } ts->ts_rssi = MS(ds_txstatus[5], AR_TxRSSICombined); ts->ts_rssi_ctl[0] = MS(ds_txstatus[0], AR_TxRSSIAnt00); ts->ts_rssi_ctl[1] = MS(ds_txstatus[0], AR_TxRSSIAnt01); ts->ts_rssi_ctl[2] = MS(ds_txstatus[0], AR_TxRSSIAnt02); ts->ts_rssi_ext[0] = MS(ds_txstatus[5], AR_TxRSSIAnt10); ts->ts_rssi_ext[1] = MS(ds_txstatus[5], AR_TxRSSIAnt11); ts->ts_rssi_ext[2] = MS(ds_txstatus[5], AR_TxRSSIAnt12); ts->ts_evm0 = AR_TxEVM0(ds_txstatus); ts->ts_evm1 = AR_TxEVM1(ds_txstatus); ts->ts_evm2 = AR_TxEVM2(ds_txstatus); ts->ts_shortretry = MS(ds_txstatus[1], AR_RTSFailCnt); ts->ts_longretry = MS(ds_txstatus[1], AR_DataFailCnt); /* * The retry count has the number of un-acked tries for the * final series used. When doing multi-rate retry we must * fixup the retry count by adding in the try counts for * each series that was fully-processed. Beware that this * takes values from the try counts in the final descriptor. * These are not required by the hardware. We assume they * are placed there by the driver as otherwise we have no * access and the driver can't do the calculation because it * doesn't know the descriptor format. */ switch (ts->ts_finaltsi) { case 3: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries2); case 2: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries1); case 1: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries0); } /* * These fields are not used. Zero these to preserve compatability * with existing drivers. */ ts->ts_virtcol = MS(ads->ds_ctl1, AR_VirtRetryCnt); ts->ts_antenna = 0; /* We don't switch antennas on Owl*/ /* handle tx trigger level changes internally */ if ((ts->ts_status & HAL_TXERR_FIFO) || - (ts->ts_flags & (HAL_TX_DATA_UNDERRUN | HAL_TX_DELIM_UNDERRUN))) + (ts->ts_flags & HAL_TX_DATA_UNDERRUN) || + (ts->ts_flags & HAL_TX_DELIM_UNDERRUN)) ar5212UpdateTxTrigLevel(ah, AH_TRUE); return HAL_OK; } #if 0 HAL_BOOL ar5416SetGlobalTxTimeout(struct ath_hal *ah, u_int tu) { struct ath_hal_5416 *ahp = AH5416(ah); if (tu > 0xFFFF) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad global tx timeout %u\n", __func__, tu); /* restore default handling */ ahp->ah_globaltxtimeout = (u_int) -1; return AH_FALSE; } OS_REG_RMW_FIELD(ah, AR_GTXTO, AR_GTXTO_TIMEOUT_LIMIT, tu); ahp->ah_globaltxtimeout = tu; return AH_TRUE; } u_int ar5416GetGlobalTxTimeout(struct ath_hal *ah) { return MS(OS_REG_READ(ah, AR_GTXTO), AR_GTXTO_TIMEOUT_LIMIT); } void ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds, u_int durUpdateEn, u_int rtsctsRate, HAL_11N_RATE_SERIES series[], u_int nseries) { struct ar5416_desc *ads = AR5416DESC(ds); HALASSERT(nseries == 4); (void)nseries; ads->ds_ctl2 = set11nTries(series, 0) | set11nTries(series, 1) | set11nTries(series, 2) | set11nTries(series, 3) | (durUpdateEn ? AR_DurUpdateEn : 0); ads->ds_ctl3 = set11nRate(series, 0) | set11nRate(series, 1) | set11nRate(series, 2) | set11nRate(series, 3); ads->ds_ctl4 = set11nPktDurRTSCTS(series, 0) | set11nPktDurRTSCTS(series, 1); ads->ds_ctl5 = set11nPktDurRTSCTS(series, 2) | set11nPktDurRTSCTS(series, 3); ads->ds_ctl7 = set11nRateFlags(series, 0) | set11nRateFlags(series, 1) | set11nRateFlags(series, 2) | set11nRateFlags(series, 3) | SM(rtsctsRate, AR_RTSCTSRate); /* * Enable RTSCTS if any of the series is flagged for RTSCTS, * but only if CTS is not enabled. */ /* * FIXME : the entire RTS/CTS handling should be moved to this * function (by passing the global RTS/CTS flags to this function). * currently it is split between this function and the * setupFiirstDescriptor. with this current implementation there * is an implicit assumption that setupFirstDescriptor is called * before this function. */ if (((series[0].RateFlags & HAL_RATESERIES_RTS_CTS) || (series[1].RateFlags & HAL_RATESERIES_RTS_CTS) || (series[2].RateFlags & HAL_RATESERIES_RTS_CTS) || (series[3].RateFlags & HAL_RATESERIES_RTS_CTS) ) && (ads->ds_ctl0 & AR_CTSEnable) == 0) { ads->ds_ctl0 |= AR_RTSEnable; ads->ds_ctl0 &= ~AR_CTSEnable; } } void ar5416Set11nAggrMiddle(struct ath_hal *ah, struct ath_desc *ds, u_int numDelims) { struct ar5416_desc *ads = AR5416DESC(ds); uint32_t *ds_txstatus = AR5416_DS_TXSTATUS(ah,ads); ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); ads->ds_ctl6 &= ~AR_PadDelim; ads->ds_ctl6 |= SM(numDelims, AR_PadDelim); ads->ds_ctl6 &= ~AR_AggrLen; /* * Clear the TxDone status here, may need to change * func name to reflect this */ ds_txstatus[9] &= ~AR_TxDone; } void ar5416Clr11nAggr(struct ath_hal *ah, struct ath_desc *ds) { struct ar5416_desc *ads = AR5416DESC(ds); ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr); ads->ds_ctl6 &= ~AR_PadDelim; ads->ds_ctl6 &= ~AR_AggrLen; } void ar5416Set11nBurstDuration(struct ath_hal *ah, struct ath_desc *ds, u_int burstDuration) { struct ar5416_desc *ads = AR5416DESC(ds); ads->ds_ctl2 &= ~AR_BurstDur; ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur); } #endif diff --git a/sys/dev/ath/ath_hal/ar5416/ar9285_attach.c b/sys/dev/ath/ath_hal/ar5416/ar9285_attach.c index a63965202185..6d4f25ec69cd 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar9285_attach.c +++ b/sys/dev/ath/ath_hal/ar5416/ar9285_attach.c @@ -1,395 +1,397 @@ /* * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting * Copyright (c) 2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_eeprom_v4k.h" /* XXX for tx/rx gain */ #include "ar5416/ar9280.h" #include "ar5416/ar9285.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar5416/ar9285.ini" #include "ar5416/ar9285v2.ini" #include "ar5416/ar9280v2.ini" /* XXX ini for tx/rx gain */ static const HAL_PERCAL_DATA ar9280_iq_cal = { /* single sample */ .calName = "IQ", .calType = IQ_MISMATCH_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MAX_LOG_COUNT, .calCollect = ar5416IQCalCollect, .calPostProc = ar5416IQCalibration }; static const HAL_PERCAL_DATA ar9280_adc_gain_cal = { /* single sample */ .calName = "ADC Gain", .calType = ADC_GAIN_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416AdcGainCalCollect, .calPostProc = ar5416AdcGainCalibration }; static const HAL_PERCAL_DATA ar9280_adc_dc_cal = { /* single sample */ .calName = "ADC DC", .calType = ADC_DC_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static const HAL_PERCAL_DATA ar9280_adc_init_dc_cal = { .calName = "ADC Init DC", .calType = ADC_DC_INIT_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = INIT_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static void ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); static HAL_BOOL ar9285FillCapabilityInfo(struct ath_hal *ah); static void ar9285WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar9285AniSetup(struct ath_hal *ah) { /* NB: disable ANI for reliable RIFS rx */ ar5212AniAttach(ah, AH_NULL, AH_NULL, AH_FALSE); } /* * Attach for an AR9285 part. */ static struct ath_hal * ar9285Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS *status) { struct ath_hal_9285 *ahp9285; struct ath_hal_5212 *ahp; struct ath_hal *ah; uint32_t val; HAL_STATUS ecode; HAL_BOOL rfStatus; HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp9285 = ath_hal_malloc(sizeof (struct ath_hal_9285)); if (ahp9285 == AH_NULL) { HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; } ahp = AH5212(ahp9285); ah = &ahp->ah_priv.h; ar5416InitState(AH5416(ah), devid, sc, st, sh, status); /* XXX override with 9285 specific state */ /* override 5416 methods for our needs */ ah->ah_setAntennaSwitch = ar9285SetAntennaSwitch; ah->ah_configPCIE = ar9285ConfigPCIE; ah->ah_setTxPower = ar9285SetTransmitPower; ah->ah_setBoardValues = ar9285SetBoardValues; AH5416(ah)->ah_cal.iqCalData.calData = &ar9280_iq_cal; AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9280_adc_gain_cal; AH5416(ah)->ah_cal.adcDcCalData.calData = &ar9280_adc_dc_cal; AH5416(ah)->ah_cal.adcDcCalInitData.calData = &ar9280_adc_init_dc_cal; AH5416(ah)->ah_cal.suppCals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; AH5416(ah)->ah_spurMitigate = ar9280SpurMitigate; AH5416(ah)->ah_writeIni = ar9285WriteIni; AH5416(ah)->ah_rx_chainmask = AR9285_DEFAULT_RXCHAINMASK; AH5416(ah)->ah_tx_chainmask = AR9285_DEFAULT_TXCHAINMASK; + + AH_PRIVATE(ah)->ah_max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1; if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't reset chip\n", __func__); ecode = HAL_EIO; goto bad; } if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", __func__); ecode = HAL_EIO; goto bad; } /* Read Revisions from Chips before taking out of reset */ val = OS_REG_READ(ah, AR_SREV); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: ID 0x%x VERSION 0x%x TYPE 0x%x REVISION 0x%x\n", __func__, MS(val, AR_XSREV_ID), MS(val, AR_XSREV_VERSION), MS(val, AR_XSREV_TYPE), MS(val, AR_XSREV_REVISION)); /* NB: include chip type to differentiate from pre-Sowl versions */ AH_PRIVATE(ah)->ah_macVersion = (val & AR_XSREV_VERSION) >> AR_XSREV_TYPE_S; AH_PRIVATE(ah)->ah_macRev = MS(val, AR_XSREV_REVISION); AH_PRIVATE(ah)->ah_ispcie = (val & AR_XSREV_TYPE_HOST_MODE) == 0; /* setup common ini data; rf backends handle remainder */ if (AR_SREV_KITE_12_OR_LATER(ah)) { HAL_INI_INIT(&ahp->ah_ini_modes, ar9285Modes_v2, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar9285Common_v2, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, ar9285PciePhy_clkreq_always_on_L1_v2, 2); } else { HAL_INI_INIT(&ahp->ah_ini_modes, ar9285Modes, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar9285Common, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, ar9285PciePhy_clkreq_always_on_L1, 2); } ar5416AttachPCIE(ah); ecode = ath_hal_v4kEepromAttach(ah); if (ecode != HAL_OK) goto bad; if (!ar5416ChipReset(ah, AH_NULL)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); ecode = HAL_EIO; goto bad; } AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); if (!ar5212ChipTest(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", __func__); ecode = HAL_ESELFTEST; goto bad; } /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Read Radio Chip Rev Extract */ AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah); switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case AR_RAD2133_SREV_MAJOR: /* Sowl: 2G/3x3 */ case AR_RAD5133_SREV_MAJOR: /* Sowl: 2+5G/3x3 */ break; default: if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5133_SREV_MAJOR; break; } #ifdef AH_DEBUG HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5G Radio Chip Rev 0x%02X is not supported by " "this driver\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; #endif } rfStatus = ar9285RfAttach(ah, &ecode); if (!rfStatus) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", __func__, ecode); goto bad; } HAL_INI_INIT(&ahp9285->ah_ini_rxgain, ar9280Modes_original_rxgain_v2, 6); /* setup txgain table */ switch (ath_hal_eepromGet(ah, AR_EEP_TXGAIN_TYPE, AH_NULL)) { case AR5416_EEP_TXGAIN_HIGH_POWER: HAL_INI_INIT(&ahp9285->ah_ini_txgain, ar9285Modes_high_power_tx_gain_v2, 6); break; case AR5416_EEP_TXGAIN_ORIG: HAL_INI_INIT(&ahp9285->ah_ini_txgain, ar9285Modes_original_tx_gain_v2, 6); break; default: HALASSERT(AH_FALSE); goto bad; /* XXX ? try to continue */ } /* * Got everything we need now to setup the capabilities. */ if (!ar9285FillCapabilityInfo(ah)) { ecode = HAL_EEREAD; goto bad; } ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error getting mac address from EEPROM\n", __func__); goto bad; } /* XXX How about the serial number ? */ /* Read Reg Domain */ AH_PRIVATE(ah)->ah_currentRD = ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, AH_NULL); /* * ah_miscMode is populated by ar5416FillCapabilityInfo() * starting from griffin. Set here to make sure that * AR_MISC_MODE_MIC_NEW_LOC_ENABLE is set before a GTK is * placed into hardware. */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, ahp->ah_miscMode); ar9285AniSetup(ah); /* Anti Noise Immunity */ ar5416InitNfHistBuff(AH5416(ah)->ah_cal.nfCalHist); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); return ah; bad: if (ah != AH_NULL) ah->ah_detach(ah); if (status) *status = ecode; return AH_NULL; } static void ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore) { if (AH_PRIVATE(ah)->ah_ispcie && !restore) { ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0); OS_DELAY(1000); OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); OS_REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); } } static void ar9285WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) { u_int modesIndex, freqIndex; int regWrites = 0; /* Setup the indices for the next set of register array writes */ /* XXX Ignore 11n dynamic mode on the AR5416 for the moment */ freqIndex = 2; if (IEEE80211_IS_CHAN_HT40(chan)) modesIndex = 3; else if (IEEE80211_IS_CHAN_108G(chan)) modesIndex = 5; else modesIndex = 4; /* Set correct Baseband to analog shift setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_modes, modesIndex, regWrites); if (AR_SREV_KITE_12_OR_LATER(ah)) { regWrites = ath_hal_ini_write(ah, &AH9285(ah)->ah_ini_txgain, modesIndex, regWrites); } regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common, 1, regWrites); } /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and * cannot be read. */ static HAL_BOOL ar9285FillCapabilityInfo(struct ath_hal *ah) { HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; if (!ar5416FillCapabilityInfo(ah)) return AH_FALSE; pCap->halNumGpioPins = 12; pCap->halWowSupport = AH_TRUE; pCap->halWowMatchPatternExact = AH_TRUE; #if 0 pCap->halWowMatchPatternDword = AH_TRUE; #endif pCap->halCSTSupport = AH_TRUE; pCap->halRifsRxSupport = AH_TRUE; pCap->halRifsTxSupport = AH_TRUE; pCap->halRtsAggrLimit = 64*1024; /* 802.11n max */ pCap->halExtChanDfsSupport = AH_TRUE; #if 0 /* XXX bluetooth */ pCap->halBtCoexSupport = AH_TRUE; #endif pCap->halAutoSleepSupport = AH_FALSE; /* XXX? */ #if 0 pCap->hal4kbSplitTransSupport = AH_FALSE; #endif pCap->halRxStbcSupport = 1; pCap->halTxStbcSupport = 1; return AH_TRUE; } HAL_BOOL ar9285SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) { #define ANTENNA0_CHAINMASK 0x1 #define ANTENNA1_CHAINMASK 0x2 struct ath_hal_5416 *ahp = AH5416(ah); /* Antenna selection is done by setting the tx/rx chainmasks approp. */ switch (settings) { case HAL_ANT_FIXED_A: /* Enable first antenna only */ ahp->ah_tx_chainmask = ANTENNA0_CHAINMASK; ahp->ah_rx_chainmask = ANTENNA0_CHAINMASK; break; case HAL_ANT_FIXED_B: /* Enable second antenna only, after checking capability */ if (AH_PRIVATE(ah)->ah_caps.halTxChainMask > ANTENNA1_CHAINMASK) ahp->ah_tx_chainmask = ANTENNA1_CHAINMASK; ahp->ah_rx_chainmask = ANTENNA1_CHAINMASK; break; case HAL_ANT_VARIABLE: /* Restore original chainmask settings */ /* XXX */ ahp->ah_tx_chainmask = AR5416_DEFAULT_TXCHAINMASK; ahp->ah_rx_chainmask = AR5416_DEFAULT_RXCHAINMASK; break; } return AH_TRUE; #undef ANTENNA0_CHAINMASK #undef ANTENNA1_CHAINMASK } static const char* ar9285Probe(uint16_t vendorid, uint16_t devid) { if (vendorid == ATHEROS_VENDOR_ID && devid == AR9285_DEVID_PCIE) return "Atheros 9285"; return AH_NULL; } AH_CHIP(AR9285, ar9285Probe, ar9285Attach);