From f3b08dd1e6ea2412807fb012d12648ce0c633697 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Sun, 3 Aug 2008 23:52:51 -0700 Subject: [PATCH 4/4] ath9k: Add new Atheros IEEE 802.11n driver To: linville@tuxdriver.com Cc: linux-wireless@vger.kernel.org, ath9k-devel@lists.ath9k.org This adds the new mac80211 11n ath9k Atheros driver. Only STA support is currently enabled and tested. Signed-off-by: Senthil Balasubramanian Signed-off-by: Felix Fietkau Signed-off-by: Christoph Hellwig Signed-off-by: Jack Howarth Signed-off-by: Jouni Malinen Signed-off-by: Sujith Manoharan Signed-off-by: Luis R. Rodriguez Signed-off-by: Pavel Roskin Signed-off-by: Vasanthakumar Thiagarajan --- MAINTAINERS | 9 + drivers/net/wireless/Kconfig | 1 + drivers/net/wireless/Makefile | 1 + drivers/net/wireless/ath9k/Kconfig | 8 + drivers/net/wireless/ath9k/Makefile | 11 + drivers/net/wireless/ath9k/ath9k.h | 1066 ++++ drivers/net/wireless/ath9k/beacon.c | 977 ++++ drivers/net/wireless/ath9k/core.c | 1961 +++++++ drivers/net/wireless/ath9k/core.h | 1097 ++++ drivers/net/wireless/ath9k/hw.c | 8563 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath9k/hw.h | 969 ++++ drivers/net/wireless/ath9k/initvals.h | 3146 +++++++++++ drivers/net/wireless/ath9k/main.c | 1533 ++++++ drivers/net/wireless/ath9k/phy.c | 436 ++ drivers/net/wireless/ath9k/phy.h | 543 ++ drivers/net/wireless/ath9k/rc.c | 2126 ++++++++ drivers/net/wireless/ath9k/rc.h | 316 ++ drivers/net/wireless/ath9k/recv.c | 1318 +++++ drivers/net/wireless/ath9k/reg.h | 1385 +++++ drivers/net/wireless/ath9k/regd.c | 1031 ++++ drivers/net/wireless/ath9k/regd.h | 412 ++ drivers/net/wireless/ath9k/regd_common.h | 1915 +++++++ drivers/net/wireless/ath9k/xmit.c | 2870 ++++++++++ 23 files changed, 31694 insertions(+), 0 deletions(-) create mode 100644 drivers/net/wireless/ath9k/Kconfig create mode 100644 drivers/net/wireless/ath9k/Makefile create mode 100644 drivers/net/wireless/ath9k/ath9k.h create mode 100644 drivers/net/wireless/ath9k/beacon.c create mode 100644 drivers/net/wireless/ath9k/core.c create mode 100644 drivers/net/wireless/ath9k/core.h create mode 100644 drivers/net/wireless/ath9k/hw.c create mode 100644 drivers/net/wireless/ath9k/hw.h create mode 100644 drivers/net/wireless/ath9k/initvals.h create mode 100644 drivers/net/wireless/ath9k/main.c create mode 100644 drivers/net/wireless/ath9k/phy.c create mode 100644 drivers/net/wireless/ath9k/phy.h create mode 100644 drivers/net/wireless/ath9k/rc.c create mode 100644 drivers/net/wireless/ath9k/rc.h create mode 100644 drivers/net/wireless/ath9k/recv.c create mode 100644 drivers/net/wireless/ath9k/reg.h create mode 100644 drivers/net/wireless/ath9k/regd.c create mode 100644 drivers/net/wireless/ath9k/regd.h create mode 100644 drivers/net/wireless/ath9k/regd_common.h create mode 100644 drivers/net/wireless/ath9k/xmit.c diff --git a/MAINTAINERS b/MAINTAINERS index 6a8a9ea..dedb573 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -714,6 +714,15 @@ L: linux-wireless@vger.kernel.org L: ath5k-devel@lists.ath5k.org S: Maintained +ATHEROS ATH9K WIRELESS DRIVER +P: Luis R. Rodriguez +M: lrodriguez@atheros.com +P: Jouni Malinen +M: jmalinen@atheros.com +L: linux-wireless@vger.kernel.org +L: ath9k-devel@lists.ath9k.org +S: Supported + ATI_REMOTE2 DRIVER P: Ville Syrjala M: syrjala@sci.fi diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index d7730bc..fcbd2cf 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -703,6 +703,7 @@ config MAC80211_HWSIM source "drivers/net/wireless/p54/Kconfig" source "drivers/net/wireless/ath5k/Kconfig" +source "drivers/net/wireless/ath9k/Kconfig" source "drivers/net/wireless/iwlwifi/Kconfig" source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/b43/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 72c314e..4a53d1d 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -64,5 +64,6 @@ obj-$(CONFIG_RT2X00) += rt2x00/ obj-$(CONFIG_P54_COMMON) += p54/ obj-$(CONFIG_ATH5K) += ath5k/ +obj-$(CONFIG_ATH9K) += ath9k/ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o diff --git a/drivers/net/wireless/ath9k/Kconfig b/drivers/net/wireless/ath9k/Kconfig new file mode 100644 index 0000000..9e19dcc --- /dev/null +++ b/drivers/net/wireless/ath9k/Kconfig @@ -0,0 +1,8 @@ +config ATH9K + tristate "Atheros 802.11n wireless cards support" + depends on PCI && MAC80211 && WLAN_80211 + ---help--- + This module adds support for wireless adapters based on + Atheros IEEE 802.11n AR5008 and AR9001 family of chipsets. + + If you choose to build a module, it'll be called ath9k. diff --git a/drivers/net/wireless/ath9k/Makefile b/drivers/net/wireless/ath9k/Makefile new file mode 100644 index 0000000..a641151 --- /dev/null +++ b/drivers/net/wireless/ath9k/Makefile @@ -0,0 +1,11 @@ +ath9k-y += hw.o \ + phy.o \ + regd.o \ + beacon.o \ + main.o \ + recv.o \ + xmit.o \ + rc.o \ + core.o + +obj-$(CONFIG_ATH9K) += ath9k.o diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h new file mode 100644 index 0000000..dc1da64 --- /dev/null +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -0,0 +1,1066 @@ +/* + * 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. + */ + +#ifndef ATH9K_H +#define ATH9K_H + +#include + +#define ATHEROS_VENDOR_ID 0x168c + +#define AR5416_DEVID_PCI 0x0023 +#define AR5416_DEVID_PCIE 0x0024 +#define AR9160_DEVID_PCI 0x0027 +#define AR9280_DEVID_PCI 0x0029 +#define AR9280_DEVID_PCIE 0x002a + +#define AR5416_AR9100_DEVID 0x000b + +#define AR_SUBVENDOR_ID_NOG 0x0e11 +#define AR_SUBVENDOR_ID_NEW_A 0x7065 + +#define ATH9K_TXERR_XRETRY 0x01 +#define ATH9K_TXERR_FILT 0x02 +#define ATH9K_TXERR_FIFO 0x04 +#define ATH9K_TXERR_XTXOP 0x08 +#define ATH9K_TXERR_TIMER_EXPIRED 0x10 + +#define ATH9K_TX_BA 0x01 +#define ATH9K_TX_PWRMGMT 0x02 +#define ATH9K_TX_DESC_CFG_ERR 0x04 +#define ATH9K_TX_DATA_UNDERRUN 0x08 +#define ATH9K_TX_DELIM_UNDERRUN 0x10 +#define ATH9K_TX_SW_ABORTED 0x40 +#define ATH9K_TX_SW_FILTERED 0x80 + +#define NBBY 8 + +struct ath_tx_status { + u32 ts_tstamp; + u16 ts_seqnum; + u8 ts_status; + u8 ts_ratecode; + u8 ts_rateindex; + int8_t ts_rssi; + u8 ts_shortretry; + u8 ts_longretry; + u8 ts_virtcol; + u8 ts_antenna; + u8 ts_flags; + int8_t ts_rssi_ctl0; + int8_t ts_rssi_ctl1; + int8_t ts_rssi_ctl2; + int8_t ts_rssi_ext0; + int8_t ts_rssi_ext1; + int8_t ts_rssi_ext2; + u8 pad[3]; + u32 ba_low; + u32 ba_high; + u32 evm0; + u32 evm1; + u32 evm2; +}; + +struct ath_rx_status { + u32 rs_tstamp; + u16 rs_datalen; + u8 rs_status; + u8 rs_phyerr; + int8_t rs_rssi; + u8 rs_keyix; + u8 rs_rate; + u8 rs_antenna; + u8 rs_more; + int8_t rs_rssi_ctl0; + int8_t rs_rssi_ctl1; + int8_t rs_rssi_ctl2; + int8_t rs_rssi_ext0; + int8_t rs_rssi_ext1; + int8_t rs_rssi_ext2; + u8 rs_isaggr; + u8 rs_moreaggr; + u8 rs_num_delims; + u8 rs_flags; + u32 evm0; + u32 evm1; + u32 evm2; +}; + +#define ATH9K_RXERR_CRC 0x01 +#define ATH9K_RXERR_PHY 0x02 +#define ATH9K_RXERR_FIFO 0x04 +#define ATH9K_RXERR_DECRYPT 0x08 +#define ATH9K_RXERR_MIC 0x10 + +#define ATH9K_RX_MORE 0x01 +#define ATH9K_RX_MORE_AGGR 0x02 +#define ATH9K_RX_GI 0x04 +#define ATH9K_RX_2040 0x08 +#define ATH9K_RX_DELIM_CRC_PRE 0x10 +#define ATH9K_RX_DELIM_CRC_POST 0x20 +#define ATH9K_RX_DECRYPT_BUSY 0x40 + +#define ATH9K_RXKEYIX_INVALID ((u8)-1) +#define ATH9K_TXKEYIX_INVALID ((u32)-1) + +struct ath_desc { + u32 ds_link; + u32 ds_data; + u32 ds_ctl0; + u32 ds_ctl1; + u32 ds_hw[20]; + union { + struct ath_tx_status tx; + struct ath_rx_status rx; + void *stats; + } ds_us; + void *ds_vdata; +} __packed; + +#define ds_txstat ds_us.tx +#define ds_rxstat ds_us.rx +#define ds_stat ds_us.stats + +#define ATH9K_TXDESC_CLRDMASK 0x0001 +#define ATH9K_TXDESC_NOACK 0x0002 +#define ATH9K_TXDESC_RTSENA 0x0004 +#define ATH9K_TXDESC_CTSENA 0x0008 +#define ATH9K_TXDESC_INTREQ 0x0010 +#define ATH9K_TXDESC_VEOL 0x0020 +#define ATH9K_TXDESC_EXT_ONLY 0x0040 +#define ATH9K_TXDESC_EXT_AND_CTL 0x0080 +#define ATH9K_TXDESC_VMF 0x0100 +#define ATH9K_TXDESC_FRAG_IS_ON 0x0200 + +#define ATH9K_RXDESC_INTREQ 0x0020 + +enum hal_capability_type { + HAL_CAP_CIPHER = 0, + HAL_CAP_TKIP_MIC, + HAL_CAP_TKIP_SPLIT, + HAL_CAP_PHYCOUNTERS, + HAL_CAP_DIVERSITY, + HAL_CAP_PSPOLL, + HAL_CAP_TXPOW, + HAL_CAP_PHYDIAG, + HAL_CAP_MCAST_KEYSRCH, + HAL_CAP_TSF_ADJUST, + HAL_CAP_WME_TKIPMIC, + HAL_CAP_RFSILENT, + HAL_CAP_ANT_CFG_2GHZ, + HAL_CAP_ANT_CFG_5GHZ +}; + +struct hal_capabilities { + u32 halChanSpreadSupport:1, + halChapTuningSupport:1, + halMicAesCcmSupport:1, + halMicCkipSupport:1, + halMicTkipSupport:1, + halCipherAesCcmSupport:1, + halCipherCkipSupport:1, + halCipherTkipSupport:1, + halVEOLSupport:1, + halBssIdMaskSupport:1, + halMcastKeySrchSupport:1, + halTsfAddSupport:1, + halChanHalfRate:1, + halChanQuarterRate:1, + halHTSupport:1, + halGTTSupport:1, + halFastCCSupport:1, + halRfSilentSupport:1, + halWowSupport:1, + halCSTSupport:1, + halEnhancedPmSupport:1, + halAutoSleepSupport:1, + hal4kbSplitTransSupport:1, + halWowMatchPatternExact:1; + u32 halWirelessModes; + u16 halTotalQueues; + u16 halKeyCacheSize; + u16 halLow5GhzChan, halHigh5GhzChan; + u16 halLow2GhzChan, halHigh2GhzChan; + u16 halNumMRRetries; + u16 halRtsAggrLimit; + u8 halTxChainMask; + u8 halRxChainMask; + u16 halTxTrigLevelMax; + u16 halRegCap; + u8 halNumGpioPins; + u8 halNumAntCfg2GHz; + u8 halNumAntCfg5GHz; +}; + +struct hal_ops_config { + int ath_hal_dma_beacon_response_time; + int ath_hal_sw_beacon_response_time; + int ath_hal_additional_swba_backoff; + int ath_hal_6mb_ack; + int ath_hal_cwmIgnoreExtCCA; + u8 ath_hal_pciePowerSaveEnable; + u8 ath_hal_pcieL1SKPEnable; + u8 ath_hal_pcieClockReq; + u32 ath_hal_pcieWaen; + int ath_hal_pciePowerReset; + u8 ath_hal_pcieRestore; + u8 ath_hal_analogShiftReg; + u8 ath_hal_htEnable; + u32 ath_hal_ofdmTrigLow; + u32 ath_hal_ofdmTrigHigh; + u32 ath_hal_cckTrigHigh; + u32 ath_hal_cckTrigLow; + u32 ath_hal_enableANI; + u8 ath_hal_noiseImmunityLvl; + u32 ath_hal_ofdmWeakSigDet; + u32 ath_hal_cckWeakSigThr; + u8 ath_hal_spurImmunityLvl; + u8 ath_hal_firStepLvl; + int8_t ath_hal_rssiThrHigh; + int8_t ath_hal_rssiThrLow; + u16 ath_hal_diversityControl; + u16 ath_hal_antennaSwitchSwap; + int ath_hal_serializeRegMode; + int ath_hal_intrMitigation; +#define SPUR_DISABLE 0 +#define SPUR_ENABLE_IOCTL 1 +#define SPUR_ENABLE_EEPROM 2 +#define AR_EEPROM_MODAL_SPURS 5 +#define AR_SPUR_5413_1 1640 +#define AR_SPUR_5413_2 1200 +#define AR_NO_SPUR 0x8000 +#define AR_BASE_FREQ_2GHZ 2300 +#define AR_BASE_FREQ_5GHZ 4900 +#define AR_SPUR_FEEQ_BOUND_HT40 19 +#define AR_SPUR_FEEQ_BOUND_HT20 10 + int ath_hal_spurMode; + u16 ath_hal_spurChans[AR_EEPROM_MODAL_SPURS][2]; +}; + +enum ath9k_tx_queue { + ATH9K_TX_QUEUE_INACTIVE = 0, + ATH9K_TX_QUEUE_DATA, + ATH9K_TX_QUEUE_BEACON, + ATH9K_TX_QUEUE_CAB, + ATH9K_TX_QUEUE_UAPSD, + ATH9K_TX_QUEUE_PSPOLL +}; + +#define ATH9K_NUM_TX_QUEUES 10 + +enum ath9k_tx_queue_subtype { + ATH9K_WME_AC_BK = 0, + ATH9K_WME_AC_BE, + ATH9K_WME_AC_VI, + ATH9K_WME_AC_VO, + ATH9K_WME_UPSD +}; + +enum ath9k_tx_queue_flags { + TXQ_FLAG_TXOKINT_ENABLE = 0x0001, + TXQ_FLAG_TXERRINT_ENABLE = 0x0001, + TXQ_FLAG_TXDESCINT_ENABLE = 0x0002, + TXQ_FLAG_TXEOLINT_ENABLE = 0x0004, + TXQ_FLAG_TXURNINT_ENABLE = 0x0008, + TXQ_FLAG_BACKOFF_DISABLE = 0x0010, + TXQ_FLAG_COMPRESSION_ENABLE = 0x0020, + TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE = 0x0040, + TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080, +}; + +struct ath9k_txq_info { + u32 tqi_ver; + enum ath9k_tx_queue_subtype tqi_subtype; + enum ath9k_tx_queue_flags tqi_qflags; + u32 tqi_priority; + u32 tqi_aifs; + u32 tqi_cwmin; + u32 tqi_cwmax; + u16 tqi_shretry; + u16 tqi_lgretry; + u32 tqi_cbrPeriod; + u32 tqi_cbrOverflowLimit; + u32 tqi_burstTime; + u32 tqi_readyTime; + u32 tqi_compBuf; +}; + +#define ATH9K_TXQ_USEDEFAULT ((u32) -1) + +#define ATH9K_DECOMP_MASK_SIZE 128 +#define ATH9K_READY_TIME_LO_BOUND 50 +#define ATH9K_READY_TIME_HI_BOUND 96 + +enum ath9k_pkt_type { + ATH9K_PKT_TYPE_NORMAL = 0, + ATH9K_PKT_TYPE_ATIM, + ATH9K_PKT_TYPE_PSPOLL, + ATH9K_PKT_TYPE_BEACON, + ATH9K_PKT_TYPE_PROBE_RESP, + ATH9K_PKT_TYPE_CHIRP, + ATH9K_PKT_TYPE_GRP_POLL, +}; + +struct ath9k_tx_queue_info { + u32 tqi_ver; + enum ath9k_tx_queue tqi_type; + enum ath9k_tx_queue_subtype tqi_subtype; + enum ath9k_tx_queue_flags tqi_qflags; + u32 tqi_priority; + u32 tqi_aifs; + u32 tqi_cwmin; + u32 tqi_cwmax; + u16 tqi_shretry; + u16 tqi_lgretry; + u32 tqi_cbrPeriod; + u32 tqi_cbrOverflowLimit; + u32 tqi_burstTime; + u32 tqi_readyTime; + u32 tqi_physCompBuf; + u32 tqi_intFlags; +}; + +enum ath9k_rx_filter { + ATH9K_RX_FILTER_UCAST = 0x00000001, + ATH9K_RX_FILTER_MCAST = 0x00000002, + ATH9K_RX_FILTER_BCAST = 0x00000004, + ATH9K_RX_FILTER_CONTROL = 0x00000008, + ATH9K_RX_FILTER_BEACON = 0x00000010, + ATH9K_RX_FILTER_PROM = 0x00000020, + ATH9K_RX_FILTER_PROBEREQ = 0x00000080, + ATH9K_RX_FILTER_PSPOLL = 0x00004000, + ATH9K_RX_FILTER_PHYERR = 0x00000100, + ATH9K_RX_FILTER_PHYRADAR = 0x00002000, +}; + +enum ath9k_int { + ATH9K_INT_RX = 0x00000001, + ATH9K_INT_RXDESC = 0x00000002, + ATH9K_INT_RXNOFRM = 0x00000008, + ATH9K_INT_RXEOL = 0x00000010, + ATH9K_INT_RXORN = 0x00000020, + ATH9K_INT_TX = 0x00000040, + ATH9K_INT_TXDESC = 0x00000080, + ATH9K_INT_TIM_TIMER = 0x00000100, + ATH9K_INT_TXURN = 0x00000800, + ATH9K_INT_MIB = 0x00001000, + ATH9K_INT_RXPHY = 0x00004000, + ATH9K_INT_RXKCM = 0x00008000, + ATH9K_INT_SWBA = 0x00010000, + ATH9K_INT_BMISS = 0x00040000, + ATH9K_INT_BNR = 0x00100000, + ATH9K_INT_TIM = 0x00200000, + ATH9K_INT_DTIM = 0x00400000, + ATH9K_INT_DTIMSYNC = 0x00800000, + ATH9K_INT_GPIO = 0x01000000, + ATH9K_INT_CABEND = 0x02000000, + ATH9K_INT_CST = 0x10000000, + ATH9K_INT_GTT = 0x20000000, + ATH9K_INT_FATAL = 0x40000000, + ATH9K_INT_GLOBAL = 0x80000000, + ATH9K_INT_BMISC = ATH9K_INT_TIM | + ATH9K_INT_DTIM | + ATH9K_INT_DTIMSYNC | + ATH9K_INT_CABEND, + ATH9K_INT_COMMON = ATH9K_INT_RXNOFRM | + ATH9K_INT_RXDESC | + ATH9K_INT_RXEOL | + ATH9K_INT_RXORN | + ATH9K_INT_TXURN | + ATH9K_INT_TXDESC | + ATH9K_INT_MIB | + ATH9K_INT_RXPHY | + ATH9K_INT_RXKCM | + ATH9K_INT_SWBA | + ATH9K_INT_BMISS | + ATH9K_INT_GPIO, + ATH9K_INT_NOCARD = 0xffffffff +}; + +struct ath9k_rate_table { + int rateCount; + u8 rateCodeToIndex[256]; + struct { + u8 valid; + u8 phy; + u32 rateKbps; + u8 rateCode; + u8 shortPreamble; + u8 dot11Rate; + u8 controlRate; + u16 lpAckDuration; + u16 spAckDuration; + } info[32]; +}; + +#define ATH9K_RATESERIES_RTS_CTS 0x0001 +#define ATH9K_RATESERIES_2040 0x0002 +#define ATH9K_RATESERIES_HALFGI 0x0004 + +struct ath9k_11n_rate_series { + u32 Tries; + u32 Rate; + u32 PktDuration; + u32 ChSel; + u32 RateFlags; +}; + +#define CHANNEL_CW_INT 0x00002 +#define CHANNEL_CCK 0x00020 +#define CHANNEL_OFDM 0x00040 +#define CHANNEL_2GHZ 0x00080 +#define CHANNEL_5GHZ 0x00100 +#define CHANNEL_PASSIVE 0x00200 +#define CHANNEL_DYN 0x00400 +#define CHANNEL_HALF 0x04000 +#define CHANNEL_QUARTER 0x08000 +#define CHANNEL_HT20 0x10000 +#define CHANNEL_HT40PLUS 0x20000 +#define CHANNEL_HT40MINUS 0x40000 + +#define CHANNEL_INTERFERENCE 0x01 +#define CHANNEL_DFS 0x02 +#define CHANNEL_4MS_LIMIT 0x04 +#define CHANNEL_DFS_CLEAR 0x08 +#define CHANNEL_DISALLOW_ADHOC 0x10 +#define CHANNEL_PER_11D_ADHOC 0x20 + +#define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM) +#define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK) +#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM) +#define CHANNEL_G_HT20 (CHANNEL_2GHZ|CHANNEL_HT20) +#define CHANNEL_A_HT20 (CHANNEL_5GHZ|CHANNEL_HT20) +#define CHANNEL_G_HT40PLUS (CHANNEL_2GHZ|CHANNEL_HT40PLUS) +#define CHANNEL_G_HT40MINUS (CHANNEL_2GHZ|CHANNEL_HT40MINUS) +#define CHANNEL_A_HT40PLUS (CHANNEL_5GHZ|CHANNEL_HT40PLUS) +#define CHANNEL_A_HT40MINUS (CHANNEL_5GHZ|CHANNEL_HT40MINUS) +#define CHANNEL_ALL \ + (CHANNEL_OFDM| \ + CHANNEL_CCK| \ + CHANNEL_2GHZ | \ + CHANNEL_5GHZ | \ + CHANNEL_HT20 | \ + CHANNEL_HT40PLUS | \ + CHANNEL_HT40MINUS) + +struct ath9k_channel { + u16 channel; + u32 channelFlags; + u8 privFlags; + int8_t maxRegTxPower; + int8_t maxTxPower; + int8_t minTxPower; + u32 chanmode; + int32_t CalValid; + bool oneTimeCalsDone; + int8_t iCoff; + int8_t qCoff; + int16_t rawNoiseFloor; + int8_t antennaMax; + u32 regDmnFlags; + u32 conformanceTestLimit[3]; /* 0:11a, 1: 11b, 2:11g */ +#ifdef ATH_NF_PER_CHAN + struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; +#endif +}; + +#define IS_CHAN_A(_c) ((((_c)->channelFlags & CHANNEL_A) == CHANNEL_A) || \ + (((_c)->channelFlags & CHANNEL_A_HT20) == CHANNEL_A_HT20) || \ + (((_c)->channelFlags & CHANNEL_A_HT40PLUS) == CHANNEL_A_HT40PLUS) || \ + (((_c)->channelFlags & CHANNEL_A_HT40MINUS) == CHANNEL_A_HT40MINUS)) +#define IS_CHAN_B(_c) (((_c)->channelFlags & CHANNEL_B) == CHANNEL_B) +#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \ + (((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \ + (((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \ + (((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS)) +#define IS_CHAN_CCK(_c) (((_c)->channelFlags & CHANNEL_CCK) != 0) +#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0) +#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0) +#define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0) +#define IS_CHAN_PASSIVE(_c) (((_c)->channelFlags & CHANNEL_PASSIVE) != 0) +#define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0) +#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0) + +/* These macros check chanmode and not channelFlags */ +#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) || \ + ((_c)->chanmode == CHANNEL_G_HT20)) +#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) || \ + ((_c)->chanmode == CHANNEL_A_HT40MINUS) || \ + ((_c)->chanmode == CHANNEL_G_HT40PLUS) || \ + ((_c)->chanmode == CHANNEL_G_HT40MINUS)) +#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c))) + +#define IS_CHAN_IN_PUBLIC_SAFETY_BAND(_c) ((_c) > 4940 && (_c) < 4990) +#define IS_CHAN_A_5MHZ_SPACED(_c) \ + ((((_c)->channelFlags & CHANNEL_5GHZ) != 0) && \ + (((_c)->channel % 20) != 0) && \ + (((_c)->channel % 10) != 0)) + +struct ath9k_keyval { + u8 kv_type; + u8 kv_pad; + u16 kv_len; + u8 kv_val[16]; + u8 kv_mic[8]; + u8 kv_txmic[8]; +}; + +enum ath9k_key_type { + ATH9K_KEY_TYPE_CLEAR, + ATH9K_KEY_TYPE_WEP, + ATH9K_KEY_TYPE_AES, + ATH9K_KEY_TYPE_TKIP, +}; + +enum ath9k_cipher { + ATH9K_CIPHER_WEP = 0, + ATH9K_CIPHER_AES_OCB = 1, + ATH9K_CIPHER_AES_CCM = 2, + ATH9K_CIPHER_CKIP = 3, + ATH9K_CIPHER_TKIP = 4, + ATH9K_CIPHER_CLR = 5, + ATH9K_CIPHER_MIC = 127 +}; + +#define AR_EEPROM_EEPCAP_COMPRESS_DIS 0x0001 +#define AR_EEPROM_EEPCAP_AES_DIS 0x0002 +#define AR_EEPROM_EEPCAP_FASTFRAME_DIS 0x0004 +#define AR_EEPROM_EEPCAP_BURST_DIS 0x0008 +#define AR_EEPROM_EEPCAP_MAXQCU 0x01F0 +#define AR_EEPROM_EEPCAP_MAXQCU_S 4 +#define AR_EEPROM_EEPCAP_HEAVY_CLIP_EN 0x0200 +#define AR_EEPROM_EEPCAP_KC_ENTRIES 0xF000 +#define AR_EEPROM_EEPCAP_KC_ENTRIES_S 12 + +#define AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND 0x0040 +#define AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN 0x0080 +#define AR_EEPROM_EEREGCAP_EN_KK_U2 0x0100 +#define AR_EEPROM_EEREGCAP_EN_KK_MIDBAND 0x0200 +#define AR_EEPROM_EEREGCAP_EN_KK_U1_ODD 0x0400 +#define AR_EEPROM_EEREGCAP_EN_KK_NEW_11A 0x0800 + +#define AR_EEPROM_EEREGCAP_EN_KK_U1_ODD_PRE4_0 0x4000 +#define AR_EEPROM_EEREGCAP_EN_KK_NEW_11A_PRE4_0 0x8000 + +#define SD_NO_CTL 0xE0 +#define NO_CTL 0xff +#define CTL_MODE_M 7 +#define CTL_11A 0 +#define CTL_11B 1 +#define CTL_11G 2 +#define CTL_2GHT20 5 +#define CTL_5GHT20 6 +#define CTL_2GHT40 7 +#define CTL_5GHT40 8 + +#define AR_EEPROM_MAC(i) (0x1d+(i)) +#define EEP_SCALE 100 +#define EEP_DELTA 10 + +#define AR_EEPROM_RFSILENT_GPIO_SEL 0x001c +#define AR_EEPROM_RFSILENT_GPIO_SEL_S 2 +#define AR_EEPROM_RFSILENT_POLARITY 0x0002 +#define AR_EEPROM_RFSILENT_POLARITY_S 1 + +#define CTRY_DEBUG 0x1ff +#define CTRY_DEFAULT 0 + +enum reg_ext_bitmap { + REG_EXT_JAPAN_MIDBAND = 1, + REG_EXT_FCC_DFS_HT40 = 2, + REG_EXT_JAPAN_NONDFS_HT40 = 3, + REG_EXT_JAPAN_DFS_HT40 = 4 +}; + +struct ath9k_country_entry { + u16 countryCode; + u16 regDmnEnum; + u16 regDmn5G; + u16 regDmn2G; + u8 isMultidomain; + u8 iso[3]; +}; + +#define REG_WRITE(_ah, _reg, _val) iowrite32(_val, _ah->ah_sh + _reg) +#define REG_READ(_ah, _reg) ioread32(_ah->ah_sh + _reg) + +#define SM(_v, _f) (((_v) << _f##_S) & _f) +#define MS(_v, _f) (((_v) & _f) >> _f##_S) +#define REG_RMW(_a, _r, _set, _clr) \ + REG_WRITE(_a, _r, (REG_READ(_a, _r) & ~(_clr)) | (_set)) +#define REG_RMW_FIELD(_a, _r, _f, _v) \ + REG_WRITE(_a, _r, \ + (REG_READ(_a, _r) & ~_f) | (((_v) << _f##_S) & _f)) +#define REG_SET_BIT(_a, _r, _f) \ + REG_WRITE(_a, _r, REG_READ(_a, _r) | _f) +#define REG_CLR_BIT(_a, _r, _f) \ + REG_WRITE(_a, _r, REG_READ(_a, _r) & ~_f) + +#define ATH9K_COMP_BUF_MAX_SIZE 9216 +#define ATH9K_COMP_BUF_ALIGN_SIZE 512 + +#define ATH9K_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 + +#define WLAN_CTRL_FRAME_SIZE (2+2+6+4) + +#define ATH_AMPDU_LIMIT_MAX (64 * 1024 - 1) +#define ATH_AMPDU_LIMIT_DEFAULT ATH_AMPDU_LIMIT_MAX + +#define IEEE80211_WEP_IVLEN 3 +#define IEEE80211_WEP_KIDLEN 1 +#define IEEE80211_WEP_CRCLEN 4 +#define IEEE80211_MAX_MPDU_LEN (3840 + FCS_LEN + \ + (IEEE80211_WEP_IVLEN + \ + IEEE80211_WEP_KIDLEN + \ + IEEE80211_WEP_CRCLEN)) +#define IEEE80211_MAX_LEN (2300 + FCS_LEN + \ + (IEEE80211_WEP_IVLEN + \ + IEEE80211_WEP_KIDLEN + \ + IEEE80211_WEP_CRCLEN)) + +#define MAX_REG_ADD_COUNT 129 +#define MAX_RATE_POWER 63 + +enum ath9k_power_mode { + ATH9K_PM_AWAKE = 0, + ATH9K_PM_FULL_SLEEP, + ATH9K_PM_NETWORK_SLEEP, + ATH9K_PM_UNDEFINED +}; + +#define HAL_ANTENNA_MIN_MODE 0 +#define HAL_ANTENNA_FIXED_A 1 +#define HAL_ANTENNA_FIXED_B 2 +#define HAL_ANTENNA_MAX_MODE 3 + +struct ath9k_mib_stats { + u32 ackrcv_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 beacons; +}; + +enum ath9k_ant_setting { + ATH9K_ANT_VARIABLE = 0, + ATH9K_ANT_FIXED_A, + ATH9K_ANT_FIXED_B +}; + +enum ath9k_opmode { + ATH9K_M_STA = 1, + ATH9K_M_IBSS = 0, + ATH9K_M_HOSTAP = 6, + ATH9K_M_MONITOR = 8 +}; + +#define ATH9K_SLOT_TIME_6 6 +#define ATH9K_SLOT_TIME_9 9 +#define ATH9K_SLOT_TIME_20 20 + +enum ath9k_ht_macmode { + ATH9K_HT_MACMODE_20 = 0, + ATH9K_HT_MACMODE_2040 = 1, +}; + +enum ath9k_ht_extprotspacing { + ATH9K_HT_EXTPROTSPACING_20 = 0, + ATH9K_HT_EXTPROTSPACING_25 = 1, +}; + +struct ath9k_ht_cwm { + enum ath9k_ht_macmode ht_macmode; + enum ath9k_ht_extprotspacing ht_extprotspacing; +}; + +enum hal_freq_band { + HAL_FREQ_BAND_5GHZ = 0, + HAL_FREQ_BAND_2GHZ = 1, +}; + +enum ath9k_ani_cmd { + ATH9K_ANI_PRESENT = 0x1, + ATH9K_ANI_NOISE_IMMUNITY_LEVEL = 0x2, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION = 0x4, + ATH9K_ANI_CCK_WEAK_SIGNAL_THR = 0x8, + ATH9K_ANI_FIRSTEP_LEVEL = 0x10, + ATH9K_ANI_SPUR_IMMUNITY_LEVEL = 0x20, + ATH9K_ANI_MODE = 0x40, + ATH9K_ANI_PHYERR_RESET = 0x80, + ATH9K_ANI_ALL = 0xff +}; + +enum phytype { + PHY_DS, + PHY_FH, + PHY_OFDM, + PHY_HT, + PHY_MAX +}; +#define PHY_CCK PHY_DS + +enum start_adhoc_option { + START_ADHOC_NO_11A, + START_ADHOC_PER_11D, + START_ADHOC_IN_11A, + START_ADHOC_IN_11B, +}; + +enum ath9k_tp_scale { + ATH9K_TP_SCALE_MAX = 0, + ATH9K_TP_SCALE_50, + ATH9K_TP_SCALE_25, + ATH9K_TP_SCALE_12, + ATH9K_TP_SCALE_MIN +}; + +enum ser_reg_mode { + SER_REG_MODE_OFF = 0, + SER_REG_MODE_ON = 1, + SER_REG_MODE_AUTO = 2, +}; + +#define AR_PHY_CCA_MAX_GOOD_VALUE -85 +#define AR_PHY_CCA_MAX_HIGH_VALUE -62 +#define AR_PHY_CCA_MIN_BAD_VALUE -121 +#define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3 +#define AR_PHY_CCA_FILTERWINDOW_LENGTH 5 + +#define ATH9K_NF_CAL_HIST_MAX 5 +#define NUM_NF_READINGS 6 + +struct ath9k_nfcal_hist { + int16_t nfCalBuffer[ATH9K_NF_CAL_HIST_MAX]; + u8 currIndex; + int16_t privNF; + u8 invalidNFcount; +}; + +struct ath9k_beacon_state { + u32 bs_nexttbtt; + u32 bs_nextdtim; + u32 bs_intval; +#define ATH9K_BEACON_PERIOD 0x0000ffff +#define ATH9K_BEACON_ENA 0x00800000 +#define ATH9K_BEACON_RESET_TSF 0x01000000 + u32 bs_dtimperiod; + u16 bs_cfpperiod; + u16 bs_cfpmaxduration; + u32 bs_cfpnext; + u16 bs_timoffset; + u16 bs_bmissthreshold; + u32 bs_sleepduration; +}; + +struct ath9k_node_stats { + u32 ns_avgbrssi; + u32 ns_avgrssi; + u32 ns_avgtxrssi; + u32 ns_avgtxrate; +}; + +#define ATH9K_RSSI_EP_MULTIPLIER (1<<7) + +enum ath9k_gpio_output_mux_type { + ATH9K_GPIO_OUTPUT_MUX_AS_OUTPUT, + ATH9K_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED, + ATH9K_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED, + ATH9K_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED, + ATH9K_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED, + ATH9K_GPIO_OUTPUT_MUX_NUM_ENTRIES +}; + +enum { + ATH9K_RESET_POWER_ON, + ATH9K_RESET_WARM, + ATH9K_RESET_COLD, +}; + +#define AH_USE_EEPROM 0x1 + +struct ath_hal { + u32 ah_magic; + u16 ah_devid; + u16 ah_subvendorid; + struct ath_softc *ah_sc; + void __iomem *ah_sh; + u16 ah_countryCode; + u32 ah_macVersion; + u16 ah_macRev; + u16 ah_phyRev; + u16 ah_analog5GhzRev; + u16 ah_analog2GhzRev; + u8 ah_decompMask[ATH9K_DECOMP_MASK_SIZE]; + u32 ah_flags; + enum ath9k_opmode ah_opmode; + struct hal_ops_config ah_config; + struct hal_capabilities ah_caps; + int16_t ah_powerLimit; + u16 ah_maxPowerLevel; + u32 ah_tpScale; + u16 ah_currentRD; + u16 ah_currentRDExt; + u16 ah_currentRDInUse; + u16 ah_currentRD5G; + u16 ah_currentRD2G; + char ah_iso[4]; + enum start_adhoc_option ah_adHocMode; + bool ah_commonMode; + struct ath9k_channel ah_channels[150]; + u32 ah_nchan; + struct ath9k_channel *ah_curchan; + u16 ah_rfsilent; + bool ah_rfkillEnabled; + bool ah_isPciExpress; + u16 ah_txTrigLevel; +#ifndef ATH_NF_PER_CHAN + struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; +#endif +}; + +enum wireless_mode { + WIRELESS_MODE_11a = 0, + WIRELESS_MODE_11b = 2, + WIRELESS_MODE_11g = 3, + WIRELESS_MODE_11NA_HT20 = 6, + WIRELESS_MODE_11NG_HT20 = 7, + WIRELESS_MODE_11NA_HT40PLUS = 8, + WIRELESS_MODE_11NA_HT40MINUS = 9, + WIRELESS_MODE_11NG_HT40PLUS = 10, + WIRELESS_MODE_11NG_HT40MINUS = 11, + WIRELESS_MODE_MAX +}; + +enum { + ATH9K_MODE_SEL_11A = 0x00001, + ATH9K_MODE_SEL_11B = 0x00002, + ATH9K_MODE_SEL_11G = 0x00004, + ATH9K_MODE_SEL_11NG_HT20 = 0x00008, + ATH9K_MODE_SEL_11NA_HT20 = 0x00010, + ATH9K_MODE_SEL_11NG_HT40PLUS = 0x00020, + ATH9K_MODE_SEL_11NG_HT40MINUS = 0x00040, + ATH9K_MODE_SEL_11NA_HT40PLUS = 0x00080, + ATH9K_MODE_SEL_11NA_HT40MINUS = 0x00100, + ATH9K_MODE_SEL_2GHZ = (ATH9K_MODE_SEL_11B | + ATH9K_MODE_SEL_11G | + ATH9K_MODE_SEL_11NG_HT20), + ATH9K_MODE_SEL_5GHZ = (ATH9K_MODE_SEL_11A | + ATH9K_MODE_SEL_11NA_HT20), + ATH9K_MODE_SEL_ALL = 0xffffffff +}; + +struct chan_centers { + u16 synth_center; + u16 ctl_center; + u16 ext_center; +}; + +int ath_hal_getcapability(struct ath_hal *ah, + enum hal_capability_type type, + u32 capability, + u32 *result); +const struct ath9k_rate_table *ath9k_hw_getratetable(struct ath_hal *ah, + u32 mode); +void ath9k_hw_detach(struct ath_hal *ah); +struct ath_hal *ath9k_hw_attach(u16 devid, + struct ath_softc *sc, + void __iomem *mem, + int *error); +bool ath9k_regd_init_channels(struct ath_hal *ah, + u32 maxchans, u32 *nchans, + u8 *regclassids, + u32 maxregids, u32 *nregids, + u16 cc, u32 modeSelect, + bool enableOutdoor, + bool enableExtendedChannels); +u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags); +enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, + enum ath9k_int ints); +bool ath9k_hw_reset(struct ath_hal *ah, enum ath9k_opmode opmode, + struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode, + u8 txchainmask, u8 rxchainmask, + enum ath9k_ht_extprotspacing extprotspacing, + bool bChannelChange, + int *status); +bool ath9k_hw_phy_disable(struct ath_hal *ah); +void ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan, + bool *isCalDone); +void ath9k_hw_ani_monitor(struct ath_hal *ah, + const struct ath9k_node_stats *stats, + struct ath9k_channel *chan); +bool ath9k_hw_calibrate(struct ath_hal *ah, + struct ath9k_channel *chan, + u8 rxchainmask, + bool longcal, + bool *isCalDone); +int16_t ath9k_hw_getchan_noise(struct ath_hal *ah, + struct ath9k_channel *chan); +void ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid, + u16 assocId); +void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits); +void ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid, + u16 assocId); +bool ath9k_hw_stoptxdma(struct ath_hal *ah, u32 q); +void ath9k_hw_reset_tsf(struct ath_hal *ah); +bool ath9k_hw_keyisvalid(struct ath_hal *ah, u16 entry); +bool ath9k_hw_keysetmac(struct ath_hal *ah, u16 entry, + const u8 *mac); +bool ath9k_hw_set_keycache_entry(struct ath_hal *ah, + u16 entry, + const struct ath9k_keyval *k, + const u8 *mac, + int xorKey); +bool ath9k_hw_set_tsfadjust(struct ath_hal *ah, + u32 setting); +void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore); +bool ath9k_hw_intrpend(struct ath_hal *ah); +bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked); +bool ath9k_hw_updatetxtriglevel(struct ath_hal *ah, + bool bIncTrigLevel); +void ath9k_hw_procmibevent(struct ath_hal *ah, + const struct ath9k_node_stats *stats); +bool ath9k_hw_setrxabort(struct ath_hal *ah, bool set); +void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode); +bool ath9k_hw_phycounters(struct ath_hal *ah); +bool ath9k_hw_keyreset(struct ath_hal *ah, u16 entry); +bool ath9k_hw_getcapability(struct ath_hal *ah, + enum hal_capability_type type, + u32 capability, + u32 *result); +bool ath9k_hw_setcapability(struct ath_hal *ah, + enum hal_capability_type type, + u32 capability, + u32 setting, + int *status); +u32 ath9k_hw_getdefantenna(struct ath_hal *ah); +void ath9k_hw_getmac(struct ath_hal *ah, u8 *mac); +void ath9k_hw_getbssidmask(struct ath_hal *ah, u8 *mask); +bool ath9k_hw_setbssidmask(struct ath_hal *ah, + const u8 *mask); +bool ath9k_hw_setpower(struct ath_hal *ah, + enum ath9k_power_mode mode); +enum ath9k_int ath9k_hw_intrget(struct ath_hal *ah); +u64 ath9k_hw_gettsf64(struct ath_hal *ah); +u32 ath9k_hw_getdefantenna(struct ath_hal *ah); +bool ath9k_hw_setslottime(struct ath_hal *ah, u32 us); +bool ath9k_hw_setantennaswitch(struct ath_hal *ah, + enum ath9k_ant_setting settings, + struct ath9k_channel *chan, + u8 *tx_chainmask, + u8 *rx_chainmask, + u8 *antenna_cfgd); +void ath9k_hw_setantenna(struct ath_hal *ah, u32 antenna); +int ath9k_hw_select_antconfig(struct ath_hal *ah, + u32 cfg); +bool ath9k_hw_puttxbuf(struct ath_hal *ah, u32 q, + u32 txdp); +bool ath9k_hw_txstart(struct ath_hal *ah, u32 q); +u16 ath9k_hw_computetxtime(struct ath_hal *ah, + const struct ath9k_rate_table *rates, + u32 frameLen, u16 rateix, + bool shortPreamble); +void ath9k_hw_set11n_ratescenario(struct ath_hal *ah, struct ath_desc *ds, + struct ath_desc *lastds, + u32 durUpdateEn, u32 rtsctsRate, + u32 rtsctsDuration, + struct ath9k_11n_rate_series series[], + u32 nseries, u32 flags); +void ath9k_hw_set11n_burstduration(struct ath_hal *ah, + struct ath_desc *ds, + u32 burstDuration); +void ath9k_hw_cleartxdesc(struct ath_hal *ah, struct ath_desc *ds); +u32 ath9k_hw_reverse_bits(u32 val, u32 n); +bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q); +u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan); +u32 ath9k_regd_get_antenna_allowed(struct ath_hal *ah, + struct ath9k_channel *chan); +u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags); +bool ath9k_hw_gettxqueueprops(struct ath_hal *ah, int q, + struct ath9k_txq_info *qInfo); +bool ath9k_hw_settxqueueprops(struct ath_hal *ah, int q, + const struct ath9k_txq_info *qInfo); +struct ath9k_channel *ath9k_regd_check_channel(struct ath_hal *ah, + const struct ath9k_channel *c); +void ath9k_hw_set11n_txdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 pktLen, enum ath9k_pkt_type type, + u32 txPower, u32 keyIx, + enum ath9k_key_type keyType, u32 flags); +bool ath9k_hw_filltxdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 segLen, bool firstSeg, + bool lastSeg, + const struct ath_desc *ds0); +u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah, + u32 *rxc_pcnt, + u32 *rxf_pcnt, + u32 *txf_pcnt); +void ath9k_hw_dmaRegDump(struct ath_hal *ah); +void ath9k_hw_beaconinit(struct ath_hal *ah, + u32 next_beacon, u32 beacon_period); +void ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah, + const struct ath9k_beacon_state *bs); +bool ath9k_hw_setuprxdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 size, u32 flags); +void ath9k_hw_putrxbuf(struct ath_hal *ah, u32 rxdp); +void ath9k_hw_rxena(struct ath_hal *ah); +void ath9k_hw_setopmode(struct ath_hal *ah); +bool ath9k_hw_setmac(struct ath_hal *ah, const u8 *mac); +void ath9k_hw_setmcastfilter(struct ath_hal *ah, u32 filter0, + u32 filter1); +u32 ath9k_hw_getrxfilter(struct ath_hal *ah); +void ath9k_hw_startpcureceive(struct ath_hal *ah); +void ath9k_hw_stoppcurecv(struct ath_hal *ah); +bool ath9k_hw_stopdmarecv(struct ath_hal *ah); +int ath9k_hw_rxprocdesc(struct ath_hal *ah, + struct ath_desc *ds, u32 pa, + struct ath_desc *nds, u64 tsf); +u32 ath9k_hw_gettxbuf(struct ath_hal *ah, u32 q); +int ath9k_hw_txprocdesc(struct ath_hal *ah, + struct ath_desc *ds); +void ath9k_hw_set11n_aggr_middle(struct ath_hal *ah, struct ath_desc *ds, + u32 numDelims); +void ath9k_hw_set11n_aggr_first(struct ath_hal *ah, struct ath_desc *ds, + u32 aggrLen); +void ath9k_hw_set11n_aggr_last(struct ath_hal *ah, struct ath_desc *ds); +bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q); +void ath9k_hw_gettxintrtxqs(struct ath_hal *ah, u32 *txqs); +void ath9k_hw_clr11n_aggr(struct ath_hal *ah, struct ath_desc *ds); +void ath9k_hw_set11n_virtualmorefrag(struct ath_hal *ah, + struct ath_desc *ds, u32 vmf); +bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit); +bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah); +int ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type, + const struct ath9k_txq_info *qInfo); +u32 ath9k_hw_numtxpending(struct ath_hal *ah, u32 q); +const char *ath9k_hw_probe(u16 vendorid, u16 devid); +bool ath9k_hw_disable(struct ath_hal *ah); +void ath9k_hw_rfdetach(struct ath_hal *ah); +void ath9k_hw_get_channel_centers(struct ath_hal *ah, + struct ath9k_channel *chan, + struct chan_centers *centers); +bool ath9k_get_channel_edges(struct ath_hal *ah, + u16 flags, u16 *low, + u16 *high); +#endif diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c new file mode 100644 index 0000000..00993f8 --- /dev/null +++ b/drivers/net/wireless/ath9k/beacon.c @@ -0,0 +1,977 @@ +/* + * 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. + */ + + /* Implementation of beacon processing. */ + +#include +#include "core.h" + +/* + * Configure parameters for the beacon queue + * + * This function will modify certain transmit queue properties depending on + * the operating mode of the station (AP or AdHoc). Parameters are AIFS + * settings and channel width min/max +*/ + +static int ath_beaconq_config(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath9k_txq_info qi; + + ath9k_hw_gettxqueueprops(ah, sc->sc_bhalq, &qi); + if (sc->sc_opmode == ATH9K_M_HOSTAP) { + /* Always burst out beacon and CAB traffic. */ + qi.tqi_aifs = 1; + qi.tqi_cwmin = 0; + qi.tqi_cwmax = 0; + } else { + /* Adhoc mode; important thing is to use 2x cwmin. */ + qi.tqi_aifs = sc->sc_beacon_qi.tqi_aifs; + qi.tqi_cwmin = 2*sc->sc_beacon_qi.tqi_cwmin; + qi.tqi_cwmax = sc->sc_beacon_qi.tqi_cwmax; + } + + if (!ath9k_hw_settxqueueprops(ah, sc->sc_bhalq, &qi)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to update h/w beacon queue parameters\n", + __func__); + return 0; + } else { + ath9k_hw_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ + return 1; + } +} + +/* + * Setup the beacon frame for transmit. + * + * Associates the beacon frame buffer with a transmit descriptor. Will set + * up all required antenna switch parameters, rate codes, and channel flags. + * Beacons are always sent out at the lowest rate, and are not retried. +*/ + +static void ath_beacon_setup(struct ath_softc *sc, + struct ath_vap *avp, struct ath_buf *bf) +{ + struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; + struct ath_hal *ah = sc->sc_ah; + struct ath_desc *ds; + int flags, antenna; + const struct ath9k_rate_table *rt; + u8 rix, rate; + int ctsrate = 0; + int ctsduration = 0; + struct ath9k_11n_rate_series series[4]; + + DPRINTF(sc, ATH_DBG_BEACON, "%s: m %p len %u\n", + __func__, skb, skb->len); + + /* setup descriptors */ + ds = bf->bf_desc; + + flags = ATH9K_TXDESC_NOACK; + + if (sc->sc_opmode == ATH9K_M_IBSS && ah->ah_caps.halVEOLSupport) { + ds->ds_link = bf->bf_daddr; /* self-linked */ + flags |= ATH9K_TXDESC_VEOL; + /* Let hardware handle antenna switching. */ + antenna = 0; + } else { + ds->ds_link = 0; + /* + * Switch antenna every beacon. + * Should only switch every beacon period, not for every + * SWBA's + * XXX assumes two antenna + */ + antenna = ((sc->ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1); + } + + ds->ds_data = bf->bf_buf_addr; + + /* + * Calculate rate code. + * XXX everything at min xmit rate + */ + rix = sc->sc_minrateix; + rt = sc->sc_currates; + rate = rt->info[rix].rateCode; + if (sc->sc_flags & ATH_PREAMBLE_SHORT) + rate |= rt->info[rix].shortPreamble; + + ath9k_hw_set11n_txdesc(ah, ds + , skb->len + FCS_LEN /* frame length */ + , ATH9K_PKT_TYPE_BEACON /* Atheros packet type */ + , avp->av_btxctl.txpower /* txpower XXX */ + , ATH9K_TXKEYIX_INVALID /* no encryption */ + , ATH9K_KEY_TYPE_CLEAR /* no encryption */ + , flags /* no ack, veol for beacons */ + ); + + /* NB: beacon's BufLen must be a multiple of 4 bytes */ + ath9k_hw_filltxdesc(ah, ds + , roundup(skb->len, 4) /* buffer length */ + , true /* first segment */ + , true /* last segment */ + , ds /* first descriptor */ + ); + + memzero(series, sizeof(struct ath9k_11n_rate_series) * 4); + series[0].Tries = 1; + series[0].Rate = rate; + series[0].ChSel = sc->sc_tx_chainmask; + series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0; + ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, + ctsrate, ctsduration, series, 4, 0); +} + +/* Move everything from the vap's mcast queue to the hardware cab queue. + * Caller must hold mcasq lock and cabq lock + * XXX MORE_DATA bit? + */ +static void empty_mcastq_into_cabq(struct ath_hal *ah, + struct ath_txq *mcastq, struct ath_txq *cabq) +{ + struct ath_buf *bfmcast; + + BUG_ON(list_empty(&mcastq->axq_q)); + + bfmcast = list_first_entry(&mcastq->axq_q, struct ath_buf, list); + + /* link the descriptors */ + if (!cabq->axq_link) + ath9k_hw_puttxbuf(ah, cabq->axq_qnum, bfmcast->bf_daddr); + else + *cabq->axq_link = bfmcast->bf_daddr; + + /* append the private vap mcast list to the cabq */ + + cabq->axq_depth += mcastq->axq_depth; + cabq->axq_totalqueued += mcastq->axq_totalqueued; + cabq->axq_linkbuf = mcastq->axq_linkbuf; + cabq->axq_link = mcastq->axq_link; + list_splice_tail_init(&mcastq->axq_q, &cabq->axq_q); + mcastq->axq_depth = 0; + mcastq->axq_totalqueued = 0; + mcastq->axq_linkbuf = NULL; + mcastq->axq_link = NULL; +} + +/* This is only run at DTIM. We move everything from the vap's mcast queue + * to the hardware cab queue. Caller must hold the mcastq lock. */ +static void trigger_mcastq(struct ath_hal *ah, + struct ath_txq *mcastq, struct ath_txq *cabq) +{ + spin_lock_bh(&cabq->axq_lock); + + if (!list_empty(&mcastq->axq_q)) + empty_mcastq_into_cabq(ah, mcastq, cabq); + + /* cabq is gated by beacon so it is safe to start here */ + if (!list_empty(&cabq->axq_q)) + ath9k_hw_txstart(ah, cabq->axq_qnum); + + spin_unlock_bh(&cabq->axq_lock); +} + +/* + * Generate beacon frame and queue cab data for a vap. + * + * Updates the contents of the beacon frame. It is assumed that the buffer for + * the beacon frame has been allocated in the ATH object, and simply needs to + * be filled for this cycle. Also, any CAB (crap after beacon?) traffic will + * be added to the beacon frame at this point. +*/ +static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + struct ath_vap *avp; + struct sk_buff *skb; + int cabq_depth; + int mcastq_depth; + int is_beacon_dtim = 0; + unsigned int curlen; + struct ath_txq *cabq; + struct ath_txq *mcastq; + avp = sc->sc_vaps[if_id]; + + mcastq = &avp->av_mcastq; + cabq = sc->sc_cabq; + + ASSERT(avp); + + if (avp->av_bcbuf == NULL) { + DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n", + __func__, avp, avp->av_bcbuf); + return NULL; + } + bf = avp->av_bcbuf; + skb = (struct sk_buff *) bf->bf_mpdu; + + /* + * Update dynamic beacon contents. If this returns + * non-zero then we need to remap the memory because + * the beacon frame changed size (probably because + * of the TIM bitmap). + */ + curlen = skb->len; + + /* XXX: spin_lock_bh should not be used here, but sparse bitches + * otherwise. We should fix sparse :) */ + spin_lock_bh(&mcastq->axq_lock); + mcastq_depth = avp->av_mcastq.axq_depth; + + if (ath_update_beacon(sc, if_id, &avp->av_boff, skb, mcastq_depth) == + 1) { + ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + bf->bf_buf_addr = ath_skb_map_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + } else { + pci_dma_sync_single_for_cpu(sc->pdev, + bf->bf_buf_addr, + skb_tailroom(skb), + PCI_DMA_TODEVICE); + } + + /* + * if the CABQ traffic from previous DTIM is pending and the current + * beacon is also a DTIM. + * 1) if there is only one vap let the cab traffic continue. + * 2) if there are more than one vap and we are using staggered + * beacons, then drain the cabq by dropping all the frames in + * the cabq so that the current vaps cab traffic can be scheduled. + */ + spin_lock_bh(&cabq->axq_lock); + cabq_depth = cabq->axq_depth; + spin_unlock_bh(&cabq->axq_lock); + + is_beacon_dtim = avp->av_boff.bo_tim[4] & 1; + + if (mcastq_depth && is_beacon_dtim && cabq_depth) { + /* + * Unlock the cabq lock as ath_tx_draintxq acquires + * the lock again which is a common function and that + * acquires txq lock inside. + */ + if (sc->sc_nvaps > 1) { + ath_tx_draintxq(sc, cabq, false); + DPRINTF(sc, ATH_DBG_BEACON, + "%s: flush previous cabq traffic\n", __func__); + } + } + + /* Construct tx descriptor. */ + ath_beacon_setup(sc, avp, bf); + + /* + * Enable the CAB queue before the beacon queue to + * insure cab frames are triggered by this beacon. + */ + if (is_beacon_dtim) + trigger_mcastq(ah, mcastq, cabq); + + spin_unlock_bh(&mcastq->axq_lock); + return bf; +} + +/* + * Startup beacon transmission for adhoc mode when they are sent entirely + * by the hardware using the self-linked descriptor + veol trick. +*/ + +static void ath_beacon_start_adhoc(struct ath_softc *sc, int if_id) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + struct ath_vap *avp; + struct sk_buff *skb; + + avp = sc->sc_vaps[if_id]; + ASSERT(avp); + + if (avp->av_bcbuf == NULL) { + DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n", + __func__, avp, avp != NULL ? avp->av_bcbuf : NULL); + return; + } + bf = avp->av_bcbuf; + skb = (struct sk_buff *) bf->bf_mpdu; + + /* Construct tx descriptor. */ + ath_beacon_setup(sc, avp, bf); + + /* NB: caller is known to have already stopped tx dma */ + ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); + ath9k_hw_txstart(ah, sc->sc_bhalq); + DPRINTF(sc, ATH_DBG_BEACON, "%s: TXDP%u = %llx (%p)\n", __func__, + sc->sc_bhalq, ito64(bf->bf_daddr), bf->bf_desc); +} + +/* + * Setup a h/w transmit queue for beacons. + * + * This function allocates an information structure (struct ath9k_txq_info) + * on the stack, sets some specific parameters (zero out channel width + * min/max, and enable aifs). The info structure does not need to be + * persistant. +*/ + +int ath_beaconq_setup(struct ath_hal *ah) +{ + struct ath9k_txq_info qi; + + memzero(&qi, sizeof(qi)); + qi.tqi_aifs = 1; + qi.tqi_cwmin = 0; + qi.tqi_cwmax = 0; + /* NB: don't enable any interrupts */ + return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); +} + + +/* + * Allocate and setup an initial beacon frame. + * + * Allocate a beacon state variable for a specific VAP instance created on + * the ATH interface. This routine also calculates the beacon "slot" for + * staggared beacons in the mBSSID case. +*/ + +int ath_beacon_alloc(struct ath_softc *sc, int if_id) +{ + struct ath_vap *avp; + struct ieee80211_hdr *wh; + struct ath_buf *bf; + struct sk_buff *skb; + + avp = sc->sc_vaps[if_id]; + ASSERT(avp); + + /* Allocate a beacon descriptor if we haven't done so. */ + if (!avp->av_bcbuf) { + /* + * Allocate beacon state for hostap/ibss. We know + * a buffer is available. + */ + + avp->av_bcbuf = list_first_entry(&sc->sc_bbuf, + struct ath_buf, list); + list_del(&avp->av_bcbuf->list); + + if (sc->sc_opmode == ATH9K_M_HOSTAP || + !sc->sc_ah->ah_caps.halVEOLSupport) { + int slot; + /* + * Assign the vap to a beacon xmit slot. As + * above, this cannot fail to find one. + */ + avp->av_bslot = 0; + for (slot = 0; slot < ATH_BCBUF; slot++) + if (sc->sc_bslot[slot] == ATH_IF_ID_ANY) { + /* + * XXX hack, space out slots to better + * deal with misses + */ + if (slot+1 < ATH_BCBUF && + sc->sc_bslot[slot+1] == + ATH_IF_ID_ANY) { + avp->av_bslot = slot+1; + break; + } + avp->av_bslot = slot; + /* NB: keep looking for a double slot */ + } + BUG_ON(sc->sc_bslot[avp->av_bslot] != ATH_IF_ID_ANY); + sc->sc_bslot[avp->av_bslot] = if_id; + sc->sc_nbcnvaps++; + } + } + + /* release the previous beacon frame , if it already exists. */ + bf = avp->av_bcbuf; + if (bf->bf_mpdu != NULL) { + skb = (struct sk_buff *)bf->bf_mpdu; + ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + } + + /* + * NB: the beacon data buffer must be 32-bit aligned; + * we assume the wbuf routines will return us something + * with this alignment (perhaps should assert). + * FIXME: Fill avp->av_boff.bo_tim,avp->av_btxctl.txpower and + * avp->av_btxctl.shortPreamble + */ + skb = ieee80211_beacon_get(sc->hw, avp->av_if_data); + if (skb == NULL) { + DPRINTF(sc, ATH_DBG_BEACON, "%s: cannot get skb\n", + __func__); + return -ENOMEM; + } + + /* + * Calculate a TSF adjustment factor required for + * staggered beacons. Note that we assume the format + * of the beacon frame leaves the tstamp field immediately + * following the header. + */ + if (avp->av_bslot > 0) { + u64 tsfadjust; + __le64 val; + int intval; + + /* FIXME: Use default value for now: Sujith */ + + intval = ATH_DEFAULT_BINTVAL; + + /* + * The beacon interval is in TU's; the TSF in usecs. + * We figure out how many TU's to add to align the + * timestamp then convert to TSF units and handle + * byte swapping before writing it in the frame. + * The hardware will then add this each time a beacon + * frame is sent. Note that we align vap's 1..N + * and leave vap 0 untouched. This means vap 0 + * has a timestamp in one beacon interval while the + * others get a timestamp aligned to the next interval. + */ + tsfadjust = (intval * (ATH_BCBUF - avp->av_bslot)) / ATH_BCBUF; + val = cpu_to_le64(tsfadjust << 10); /* TU->TSF */ + + DPRINTF(sc, ATH_DBG_BEACON, + "%s: %s beacons, bslot %d intval %u tsfadjust %llu\n", + __func__, "stagger", + avp->av_bslot, intval, (unsigned long long)tsfadjust); + + wh = (struct ieee80211_hdr *)skb->data; + memcpy(&wh[1], &val, sizeof(val)); + } + + bf->bf_buf_addr = ath_skb_map_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + bf->bf_mpdu = skb; + + return 0; +} + +/* + * Reclaim beacon resources and return buffer to the pool. + * + * Checks the VAP to put the beacon frame buffer back to the ATH object + * queue, and de-allocates any wbuf frames that were sent as CAB traffic. +*/ + +void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp) +{ + if (avp->av_bcbuf != NULL) { + struct ath_buf *bf; + + if (avp->av_bslot != -1) { + sc->sc_bslot[avp->av_bslot] = ATH_IF_ID_ANY; + sc->sc_nbcnvaps--; + } + + bf = avp->av_bcbuf; + if (bf->bf_mpdu != NULL) { + struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; + ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + } + list_add_tail(&bf->list, &sc->sc_bbuf); + + avp->av_bcbuf = NULL; + } +} + +/* + * Reclaim beacon resources and return buffer to the pool. + * + * This function will free any wbuf frames that are still attached to the + * beacon buffers in the ATH object. Note that this does not de-allocate + * any wbuf objects that are in the transmit queue and have not yet returned + * to the ATH object. +*/ + +void ath_beacon_free(struct ath_softc *sc) +{ + struct ath_buf *bf; + + list_for_each_entry(bf, &sc->sc_bbuf, list) { + if (bf->bf_mpdu != NULL) { + struct sk_buff *skb = (struct sk_buff *) bf->bf_mpdu; + ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + } + } +} + +/* + * Tasklet for Sending Beacons + * + * Transmit one or more beacon frames at SWBA. Dynamic updates to the frame + * contents are done as needed and the slot time is also adjusted based on + * current state. + * + * This tasklet is not scheduled, it's called in ISR context. +*/ + +void ath9k_beacon_tasklet(unsigned long data) +{ +#define TSF_TO_TU(_h,_l) \ + ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) + + struct ath_softc *sc = (struct ath_softc *)data; + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf = NULL; + int slot, if_id; + u32 bfaddr; + u32 rx_clear = 0, rx_frame = 0, tx_frame = 0; + u32 show_cycles = 0; + u32 bc = 0; /* beacon count */ + u64 tsf; + u32 tsftu; + u16 intval; + + if (sc->sc_noreset) { + show_cycles = ath9k_hw_GetMibCycleCountsPct(ah, + &rx_clear, + &rx_frame, + &tx_frame); + } + + /* + * Check if the previous beacon has gone out. If + * not don't try to post another, skip this period + * and wait for the next. Missed beacons indicate + * a problem and should not occur. If we miss too + * many consecutive beacons reset the device. + */ + if (ath9k_hw_numtxpending(ah, sc->sc_bhalq) != 0) { + sc->sc_bmisscount++; + /* XXX: doth needs the chanchange IE countdown decremented. + * We should consider adding a mac80211 call to indicate + * a beacon miss so appropriate action could be taken + * (in that layer). + */ + if (sc->sc_bmisscount < BSTUCK_THRESH) { + if (sc->sc_noreset) { + DPRINTF(sc, ATH_DBG_BEACON, + "%s: missed %u consecutive beacons\n", + __func__, sc->sc_bmisscount); + if (show_cycles) { + /* + * Display cycle counter stats + * from HW to aide in debug of + * stickiness. + */ + DPRINTF(sc, + ATH_DBG_BEACON, + "%s: busy times: rx_clear=%d, " + "rx_frame=%d, tx_frame=%d\n", + __func__, rx_clear, rx_frame, + tx_frame); + } else { + DPRINTF(sc, + ATH_DBG_BEACON, + "%s: unable to obtain " + "busy times\n", __func__); + } + } else { + DPRINTF(sc, ATH_DBG_BEACON, + "%s: missed %u consecutive beacons\n", + __func__, sc->sc_bmisscount); + } + } else if (sc->sc_bmisscount >= BSTUCK_THRESH) { + if (sc->sc_noreset) { + if (sc->sc_bmisscount == BSTUCK_THRESH) { + DPRINTF(sc, + ATH_DBG_BEACON, + "%s: beacon is officially " + "stuck\n", __func__); + ath9k_hw_dmaRegDump(ah); + } + } else { + DPRINTF(sc, ATH_DBG_BEACON, + "%s: beacon is officially stuck\n", + __func__); + ath_bstuck_process(sc); + } + } + + return; + } + if (sc->sc_bmisscount != 0) { + if (sc->sc_noreset) { + DPRINTF(sc, + ATH_DBG_BEACON, + "%s: resume beacon xmit after %u misses\n", + __func__, sc->sc_bmisscount); + } else { + DPRINTF(sc, ATH_DBG_BEACON, + "%s: resume beacon xmit after %u misses\n", + __func__, sc->sc_bmisscount); + } + sc->sc_bmisscount = 0; + } + + /* + * Generate beacon frames. we are sending frames + * staggered so calculate the slot for this frame based + * on the tsf to safeguard against missing an swba. + */ + + /* FIXME: Use default value for now - Sujith */ + intval = ATH_DEFAULT_BINTVAL; + + tsf = ath9k_hw_gettsf64(ah); + tsftu = TSF_TO_TU(tsf>>32, tsf); + slot = ((tsftu % intval) * ATH_BCBUF) / intval; + if_id = sc->sc_bslot[(slot + 1) % ATH_BCBUF]; + DPRINTF(sc, ATH_DBG_BEACON, + "%s: slot %d [tsf %llu tsftu %u intval %u] if_id %d\n", + __func__, slot, (unsigned long long) tsf, tsftu, + intval, if_id); + bfaddr = 0; + if (if_id != ATH_IF_ID_ANY) { + bf = ath_beacon_generate(sc, if_id); + if (bf != NULL) { + bfaddr = bf->bf_daddr; + bc = 1; + } + } + /* + * Handle slot time change when a non-ERP station joins/leaves + * an 11g network. The 802.11 layer notifies us via callback, + * we mark updateslot, then wait one beacon before effecting + * the change. This gives associated stations at least one + * beacon interval to note the state change. + * + * NB: The slot time change state machine is clocked according + * to whether we are bursting or staggering beacons. We + * recognize the request to update and record the current + * slot then don't transition until that slot is reached + * again. If we miss a beacon for that slot then we'll be + * slow to transition but we'll be sure at least one beacon + * interval has passed. When bursting slot is always left + * set to ATH_BCBUF so this check is a noop. + */ + /* XXX locking */ + if (sc->sc_updateslot == UPDATE) { + sc->sc_updateslot = COMMIT; /* commit next beacon */ + sc->sc_slotupdate = slot; + } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) + ath_setslottime(sc); /* commit change to hardware */ + + if (bfaddr != 0) { + /* + * Stop any current dma and put the new frame(s) on the queue. + * This should never fail since we check above that no frames + * are still pending on the queue. + */ + if (!ath9k_hw_stoptxdma(ah, sc->sc_bhalq)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: beacon queue %u did not stop?\n", + __func__, sc->sc_bhalq); + /* NB: the HAL still stops DMA, so proceed */ + } + + /* NB: cabq traffic should already be queued and primed */ + ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bfaddr); + ath9k_hw_txstart(ah, sc->sc_bhalq); + + sc->ast_be_xmit += bc; /* XXX per-vap? */ + } +#undef TSF_TO_TU +} + +/* + * Tasklet for Beacon Stuck processing + * + * Processing for Beacon Stuck. + * Basically calls the ath_internal_reset function to reset the chip. +*/ + +void ath_bstuck_process(struct ath_softc *sc) +{ + DPRINTF(sc, ATH_DBG_BEACON, + "%s: stuck beacon; resetting (bmiss count %u)\n", + __func__, sc->sc_bmisscount); + ath_internal_reset(sc); +} + +/* + * Configure the beacon and sleep timers. + * + * When operating as an AP this resets the TSF and sets + * up the hardware to notify us when we need to issue beacons. + * + * When operating in station mode this sets up the beacon + * timers according to the timestamp of the last received + * beacon and the current TSF, configures PCF and DTIM + * handling, programs the sleep registers so the hardware + * will wakeup in time to receive beacons, and configures + * the beacon miss handling so we'll receive a BMISS + * interrupt when we stop seeing beacons from the AP + * we've associated with. + */ + +void ath_beacon_config(struct ath_softc *sc, int if_id) +{ +#define TSF_TO_TU(_h,_l) \ + ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) + struct ath_hal *ah = sc->sc_ah; + u32 nexttbtt, intval; + struct ath_beacon_config conf; + enum ath9k_opmode av_opmode; + + if (if_id != ATH_IF_ID_ANY) + av_opmode = sc->sc_vaps[if_id]->av_opmode; + else + av_opmode = sc->sc_opmode; + + memzero(&conf, sizeof(struct ath_beacon_config)); + + /* FIXME: Use default values for now - Sujith */ + /* Query beacon configuration first */ + /* + * Protocol stack doesn't support dynamic beacon configuration, + * use default configurations. + */ + conf.beacon_interval = ATH_DEFAULT_BINTVAL; + conf.listen_interval = 1; + conf.dtim_period = conf.beacon_interval; + conf.dtim_count = 1; + conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval; + + /* extract tstamp from last beacon and convert to TU */ + nexttbtt = TSF_TO_TU(get_unaligned_le32(conf.u.last_tstamp + 4), + get_unaligned_le32(conf.u.last_tstamp)); + /* XXX conditionalize multi-bss support? */ + if (sc->sc_opmode == ATH9K_M_HOSTAP) { + /* + * For multi-bss ap support beacons are either staggered + * evenly over N slots or burst together. For the former + * arrange for the SWBA to be delivered for each slot. + * Slots that are not occupied will generate nothing. + */ + /* NB: the beacon interval is kept internally in TU's */ + intval = conf.beacon_interval & ATH9K_BEACON_PERIOD; + intval /= ATH_BCBUF; /* for staggered beacons */ + } else { + intval = conf.beacon_interval & ATH9K_BEACON_PERIOD; + } + + if (nexttbtt == 0) /* e.g. for ap mode */ + nexttbtt = intval; + else if (intval) /* NB: can be 0 for monitor mode */ + nexttbtt = roundup(nexttbtt, intval); + DPRINTF(sc, ATH_DBG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", + __func__, nexttbtt, intval, conf.beacon_interval); + /* Check for ATH9K_M_HOSTAP and sc_nostabeacons for WDS client */ + if (sc->sc_opmode == ATH9K_M_STA) { + struct ath9k_beacon_state bs; + u64 tsf; + u32 tsftu; + int dtimperiod, dtimcount, sleepduration; + int cfpperiod, cfpcount; + + /* + * Setup dtim and cfp parameters according to + * last beacon we received (which may be none). + */ + dtimperiod = conf.dtim_period; + if (dtimperiod <= 0) /* NB: 0 if not known */ + dtimperiod = 1; + dtimcount = conf.dtim_count; + if (dtimcount >= dtimperiod) /* NB: sanity check */ + dtimcount = 0; /* XXX? */ + cfpperiod = 1; /* NB: no PCF support yet */ + cfpcount = 0; + + sleepduration = conf.listen_interval * intval; + if (sleepduration <= 0) + sleepduration = intval; + +#define FUDGE 2 + /* + * Pull nexttbtt forward to reflect the current + * TSF and calculate dtim+cfp state for the result. + */ + tsf = ath9k_hw_gettsf64(ah); + tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; + do { + nexttbtt += intval; + if (--dtimcount < 0) { + dtimcount = dtimperiod - 1; + if (--cfpcount < 0) + cfpcount = cfpperiod - 1; + } + } while (nexttbtt < tsftu); +#undef FUDGE + memzero(&bs, sizeof(bs)); + bs.bs_intval = intval; + bs.bs_nexttbtt = nexttbtt; + bs.bs_dtimperiod = dtimperiod*intval; + bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; + bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; + bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; + bs.bs_cfpmaxduration = 0; + /* + * Calculate the number of consecutive beacons to miss + * before taking a BMISS interrupt. The configuration + * is specified in TU so we only need calculate based + * on the beacon interval. Note that we clamp the + * result to at most 15 beacons. + */ + if (sleepduration > intval) { + bs.bs_bmissthreshold = + conf.listen_interval * + ATH_DEFAULT_BMISS_LIMIT / 2; + } else { + bs.bs_bmissthreshold = + DIV_ROUND_UP(conf.bmiss_timeout, intval); + if (bs.bs_bmissthreshold > 15) + bs.bs_bmissthreshold = 15; + else if (bs.bs_bmissthreshold <= 0) + bs.bs_bmissthreshold = 1; + } + + /* + * Calculate sleep duration. The configuration is + * given in ms. We insure a multiple of the beacon + * period is used. Also, if the sleep duration is + * greater than the DTIM period then it makes senses + * to make it a multiple of that. + * + * XXX fixed at 100ms + */ + + bs.bs_sleepduration = + roundup(IEEE80211_MS_TO_TU(100), sleepduration); + if (bs.bs_sleepduration > bs.bs_dtimperiod) + bs.bs_sleepduration = bs.bs_dtimperiod; + + DPRINTF(sc, ATH_DBG_BEACON, + "%s: tsf %llu " + "tsf:tu %u " + "intval %u " + "nexttbtt %u " + "dtim %u " + "nextdtim %u " + "bmiss %u " + "sleep %u " + "cfp:period %u " + "maxdur %u " + "next %u " + "timoffset %u\n" + , __func__ + , (unsigned long long)tsf, tsftu + , bs.bs_intval + , bs.bs_nexttbtt + , bs.bs_dtimperiod + , bs.bs_nextdtim + , bs.bs_bmissthreshold + , bs.bs_sleepduration + , bs.bs_cfpperiod + , bs.bs_cfpmaxduration + , bs.bs_cfpnext + , bs.bs_timoffset + ); + + ath9k_hw_set_interrupts(ah, 0); + ath9k_hw_set_sta_beacon_timers(ah, &bs); + sc->sc_imask |= ATH9K_INT_BMISS; + ath9k_hw_set_interrupts(ah, sc->sc_imask); + } else { + u64 tsf; + u32 tsftu; + ath9k_hw_set_interrupts(ah, 0); + if (nexttbtt == intval) + intval |= ATH9K_BEACON_RESET_TSF; + if (sc->sc_opmode == ATH9K_M_IBSS) { + /* + * Pull nexttbtt forward to reflect the current + * TSF . + */ +#define FUDGE 2 + if (!(intval & ATH9K_BEACON_RESET_TSF)) { + tsf = ath9k_hw_gettsf64(ah); + tsftu = TSF_TO_TU((u32)(tsf>>32), + (u32)tsf) + FUDGE; + do { + nexttbtt += intval; + } while (nexttbtt < tsftu); + } +#undef FUDGE + DPRINTF(sc, ATH_DBG_BEACON, + "%s: IBSS nexttbtt %u intval %u (%u)\n", + __func__, nexttbtt, + intval & ~ATH9K_BEACON_RESET_TSF, + conf.beacon_interval); + + /* + * In IBSS mode enable the beacon timers but only + * enable SWBA interrupts if we need to manually + * prepare beacon frames. Otherwise we use a + * self-linked tx descriptor and let the hardware + * deal with things. + */ + intval |= ATH9K_BEACON_ENA; + if (!ah->ah_caps.halVEOLSupport) + sc->sc_imask |= ATH9K_INT_SWBA; + ath_beaconq_config(sc); + } else if (sc->sc_opmode == ATH9K_M_HOSTAP) { + /* + * In AP mode we enable the beacon timers and + * SWBA interrupts to prepare beacon frames. + */ + intval |= ATH9K_BEACON_ENA; + sc->sc_imask |= ATH9K_INT_SWBA; /* beacon prepare */ + ath_beaconq_config(sc); + } + ath9k_hw_beaconinit(ah, nexttbtt, intval); + sc->sc_bmisscount = 0; + ath9k_hw_set_interrupts(ah, sc->sc_imask); + /* + * When using a self-linked beacon descriptor in + * ibss mode load it once here. + */ + if (sc->sc_opmode == ATH9K_M_IBSS && ah->ah_caps.halVEOLSupport) + ath_beacon_start_adhoc(sc, 0); + } +#undef TSF_TO_TU +} + +/* Function to collect beacon rssi data and resync beacon if necessary */ + +void ath_beacon_sync(struct ath_softc *sc, int if_id) +{ + /* + * Resync beacon timers using the tsf of the + * beacon frame we just received. + */ + ath_beacon_config(sc, if_id); + sc->sc_beacons = 1; +} diff --git a/drivers/net/wireless/ath9k/core.c b/drivers/net/wireless/ath9k/core.c new file mode 100644 index 0000000..e8b3f1f --- /dev/null +++ b/drivers/net/wireless/ath9k/core.c @@ -0,0 +1,1961 @@ +/* + * 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. + */ + + /* Implementation of the main "ATH" layer. */ + +#include "core.h" +#include "regd.h" + +static int ath_outdoor; /* enable outdoor use */ + +static const u8 ath_bcast_mac[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static u32 ath_chainmask_sel_up_rssi_thres = + ATH_CHAINMASK_SEL_UP_RSSI_THRES; +static u32 ath_chainmask_sel_down_rssi_thres = + ATH_CHAINMASK_SEL_DOWN_RSSI_THRES; +static u32 ath_chainmask_sel_period = + ATH_CHAINMASK_SEL_TIMEOUT; + +/* return bus cachesize in 4B word units */ + +static void bus_read_cachesize(struct ath_softc *sc, int *csz) +{ + u8 u8tmp; + + pci_read_config_byte(sc->pdev, PCI_CACHE_LINE_SIZE, (u8 *)&u8tmp); + *csz = (int)u8tmp; + + /* + * This check was put in to avoid "unplesant" consequences if + * the bootrom has not fully initialized all PCI devices. + * Sometimes the cache line size register is not set + */ + + if (*csz == 0) + *csz = DEFAULT_CACHELINE >> 2; /* Use the default size */ +} + +/* + * Set current operating mode + * + * This function initializes and fills the rate table in the ATH object based + * on the operating mode. The blink rates are also set up here, although + * they have been superceeded by the ath_led module. +*/ + +static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode) +{ + const struct ath9k_rate_table *rt; + int i; + + memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); + rt = sc->sc_rates[mode]; + BUG_ON(!rt); + + for (i = 0; i < rt->rateCount; i++) + sc->sc_rixmap[rt->info[i].rateCode] = (u8) i; + + memzero(sc->sc_hwmap, sizeof(sc->sc_hwmap)); + for (i = 0; i < 256; i++) { + u8 ix = rt->rateCodeToIndex[i]; + + if (ix == 0xff) + continue; + + sc->sc_hwmap[i].ieeerate = + rt->info[ix].dot11Rate & IEEE80211_RATE_VAL; + sc->sc_hwmap[i].rateKbps = rt->info[ix].rateKbps; + + if (rt->info[ix].shortPreamble || + rt->info[ix].phy == PHY_OFDM) { + /* XXX: Handle this */ + } + + /* NB: this uses the last entry if the rate isn't found */ + /* XXX beware of overlow */ + } + sc->sc_currates = rt; + sc->sc_curmode = mode; + /* + * All protection frames are transmited at 2Mb/s for + * 11g, otherwise at 1Mb/s. + * XXX select protection rate index from rate table. + */ + sc->sc_protrix = (mode == WIRELESS_MODE_11g ? 1 : 0); + /* rate index used to send mgt frames */ + sc->sc_minrateix = 0; +} + +/* + * Select Rate Table + * + * Based on the wireless mode passed in, the rate table in the ATH object + * is set to the mode specific rate table. This also calls the callback + * function to set the rate in the protocol layer object. +*/ + +static int ath_rate_setup(struct ath_softc *sc, enum wireless_mode mode) +{ + struct ath_hal *ah = sc->sc_ah; + const struct ath9k_rate_table *rt; + + switch (mode) { + case WIRELESS_MODE_11a: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11A); + break; + case WIRELESS_MODE_11b: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11B); + break; + case WIRELESS_MODE_11g: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11G); + break; + case WIRELESS_MODE_11NA_HT20: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NA_HT20); + break; + case WIRELESS_MODE_11NG_HT20: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NG_HT20); + break; + case WIRELESS_MODE_11NA_HT40PLUS: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NA_HT40PLUS); + break; + case WIRELESS_MODE_11NA_HT40MINUS: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, + ATH9K_MODE_SEL_11NA_HT40MINUS); + break; + case WIRELESS_MODE_11NG_HT40PLUS: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NG_HT40PLUS); + break; + case WIRELESS_MODE_11NG_HT40MINUS: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, + ATH9K_MODE_SEL_11NG_HT40MINUS); + break; + default: + DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid mode %u\n", + __func__, mode); + return 0; + } + rt = sc->sc_rates[mode]; + if (rt == NULL) + return 0; + + /* setup rate set in 802.11 protocol layer */ + ath_setup_rate(sc, mode, NORMAL_RATE, rt); + + return 1; +} + +/* + * Set up channel list + */ +static int ath_setup_channels(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + int nchan, i, a = 0, b = 0; + u8 regclassids[ATH_REGCLASSIDS_MAX]; + u32 nregclass = 0; + struct ieee80211_supported_band *band_2ghz; + struct ieee80211_supported_band *band_5ghz; + struct ieee80211_channel *chan_2ghz; + struct ieee80211_channel *chan_5ghz; + struct ath9k_channel *c; + + /* Fill in ah->ah_channels */ + if (!ath9k_regd_init_channels(ah, + ATH_CHAN_MAX, + (u32 *)&nchan, + regclassids, + ATH_REGCLASSIDS_MAX, + &nregclass, + CTRY_DEFAULT, + ATH9K_MODE_SEL_ALL, + false, + 1)) { + u32 rd = ah->ah_currentRD; + + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to collect channel list; " + "regdomain likely %u country code %u\n", + __func__, rd, CTRY_DEFAULT); + return -EINVAL; + } + + band_2ghz = &sc->sbands[IEEE80211_BAND_2GHZ]; + band_5ghz = &sc->sbands[IEEE80211_BAND_5GHZ]; + chan_2ghz = sc->channels[IEEE80211_BAND_2GHZ]; + chan_5ghz = sc->channels[IEEE80211_BAND_5GHZ]; + + for (i = 0; i < nchan; i++) { + c = &ah->ah_channels[i]; + if (IS_CHAN_2GHZ(c)) { + chan_2ghz[a].band = IEEE80211_BAND_2GHZ; + chan_2ghz[a].center_freq = c->channel; + chan_2ghz[a].max_power = c->maxTxPower; + + if (c->privFlags & CHANNEL_DISALLOW_ADHOC) + chan_2ghz[a].flags |= + IEEE80211_CHAN_NO_IBSS; + if (c->channelFlags & CHANNEL_PASSIVE) + chan_2ghz[a].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + + band_2ghz->n_channels = ++a; + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: 2MHz channel: %d, " + "channelFlags: 0x%x\n", + __func__, + c->channel, + c->channelFlags); + } else if (IS_CHAN_5GHZ(c)) { + chan_5ghz[b].band = IEEE80211_BAND_5GHZ; + chan_5ghz[b].center_freq = c->channel; + chan_5ghz[b].max_power = c->maxTxPower; + + if (c->privFlags & CHANNEL_DISALLOW_ADHOC) + chan_5ghz[b].flags |= + IEEE80211_CHAN_NO_IBSS; + if (c->channelFlags & CHANNEL_PASSIVE) + chan_5ghz[b].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + + band_5ghz->n_channels = ++b; + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: 5MHz channel: %d, " + "channelFlags: 0x%x\n", + __func__, + c->channel, + c->channelFlags); + } + } + + return 0; +} + +/* + * Determine mode from channel flags + * + * This routine will provide the enumerated WIRELESSS_MODE value based + * on the settings of the channel flags. If ho valid set of flags + * exist, the lowest mode (11b) is selected. +*/ + +static enum wireless_mode ath_chan2mode(struct ath9k_channel *chan) +{ + if (chan->chanmode == CHANNEL_A) + return WIRELESS_MODE_11a; + else if (chan->chanmode == CHANNEL_G) + return WIRELESS_MODE_11g; + else if (chan->chanmode == CHANNEL_B) + return WIRELESS_MODE_11b; + else if (chan->chanmode == CHANNEL_A_HT20) + return WIRELESS_MODE_11NA_HT20; + else if (chan->chanmode == CHANNEL_G_HT20) + return WIRELESS_MODE_11NG_HT20; + else if (chan->chanmode == CHANNEL_A_HT40PLUS) + return WIRELESS_MODE_11NA_HT40PLUS; + else if (chan->chanmode == CHANNEL_A_HT40MINUS) + return WIRELESS_MODE_11NA_HT40MINUS; + else if (chan->chanmode == CHANNEL_G_HT40PLUS) + return WIRELESS_MODE_11NG_HT40PLUS; + else if (chan->chanmode == CHANNEL_G_HT40MINUS) + return WIRELESS_MODE_11NG_HT40MINUS; + + /* NB: should not get here */ + return WIRELESS_MODE_11b; +} + +/* + * Change Channels + * + * Performs the actions to change the channel in the hardware, and set up + * the current operating mode for the new channel. +*/ + +static void ath_chan_change(struct ath_softc *sc, struct ath9k_channel *chan) +{ + enum wireless_mode mode; + + mode = ath_chan2mode(chan); + + ath_rate_setup(sc, mode); + ath_setcurmode(sc, mode); +} + +/* + * Stop the device, grabbing the top-level lock to protect + * against concurrent entry through ath_init (which can happen + * if another thread does a system call and the thread doing the + * stop is preempted). + */ + +static int ath_stop(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: invalid %u\n", + __func__, sc->sc_invalid); + + /* + * Shutdown the hardware and driver: + * stop output from above + * reset 802.11 state machine + * (sends station deassoc/deauth frames) + * turn off timers + * disable interrupts + * clear transmit machinery + * clear receive machinery + * turn off the radio + * reclaim beacon resources + * + * Note that some of this work is not possible if the + * hardware is gone (invalid). + */ + + if (!sc->sc_invalid) + ath9k_hw_set_interrupts(ah, 0); + ath_draintxq(sc, false); + if (!sc->sc_invalid) { + ath_stoprecv(sc); + ath9k_hw_phy_disable(ah); + } else + sc->sc_rxlink = NULL; + + return 0; +} + +/* + * Start Scan + * + * This function is called when starting a channel scan. It will perform + * power save wakeup processing, set the filter for the scan, and get the + * chip ready to send broadcast packets out during the scan. +*/ + +void ath_scan_start(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + u32 rfilt; + u32 now = (u32) jiffies_to_msecs(get_timestamp()); + + sc->sc_scanning = 1; + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); + ath9k_hw_write_associd(ah, ath_bcast_mac, 0); + + /* Restore previous power management state. */ + + DPRINTF(sc, ATH_DBG_CONFIG, "%d.%03d | %s: RX filter 0x%x aid 0\n", + now / 1000, now % 1000, __func__, rfilt); +} + +/* + * Scan End + * + * This routine is called by the upper layer when the scan is completed. This + * will set the filters back to normal operating mode, set the BSSID to the + * correct value, and restore the power save state. +*/ + +void ath_scan_end(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + u32 rfilt; + u32 now = (u32) jiffies_to_msecs(get_timestamp()); + + sc->sc_scanning = 0; + /* Request for a full reset due to rx packet filter changes */ + sc->sc_full_reset = 1; + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); + ath9k_hw_write_associd(ah, sc->sc_curbssid, sc->sc_curaid); + + DPRINTF(sc, ATH_DBG_CONFIG, "%d.%03d | %s: RX filter 0x%x aid 0x%x\n", + now / 1000, now % 1000, __func__, rfilt, sc->sc_curaid); +} + +/* + * Set the current channel + * + * Set/change channels. If the channel is really being changed, it's done + * by reseting the chip. To accomplish this we must first cleanup any pending + * DMA, then restart stuff after a la ath_init. +*/ +int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan) +{ + struct ath_hal *ah = sc->sc_ah; + bool fastcc = true, stopped; + enum ath9k_ht_macmode ht_macmode; + + if (sc->sc_invalid) /* if the device is invalid or removed */ + return -EIO; + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: %u (%u MHz) -> %u (%u MHz), cflags:%x\n", + __func__, + ath9k_hw_mhz2ieee(ah, sc->sc_curchan.channel, + sc->sc_curchan.channelFlags), + sc->sc_curchan.channel, + ath9k_hw_mhz2ieee(ah, hchan->channel, hchan->channelFlags), + hchan->channel, hchan->channelFlags); + + ht_macmode = ath_cwm_macmode(sc); + + if (hchan->channel != sc->sc_curchan.channel || + hchan->channelFlags != sc->sc_curchan.channelFlags || + sc->sc_update_chainmask || sc->sc_full_reset) { + int status; + /* + * This is only performed if the channel settings have + * actually changed. + * + * To switch channels clear any pending DMA operations; + * wait long enough for the RX fifo to drain, reset the + * hardware at the new frequency, and then re-enable + * the relevant bits of the h/w. + */ + ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */ + ath_draintxq(sc, false); /* clear pending tx frames */ + stopped = ath_stoprecv(sc); /* turn off frame recv */ + + /* XXX: do not flush receive queue here. We don't want + * to flush data frames already in queue because of + * changing channel. */ + + if (!stopped || sc->sc_full_reset) + fastcc = false; + + spin_lock_bh(&sc->sc_resetlock); + if (!ath9k_hw_reset(ah, sc->sc_opmode, hchan, + ht_macmode, sc->sc_tx_chainmask, + sc->sc_rx_chainmask, + sc->sc_ht_extprotspacing, + fastcc, &status)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to reset channel %u (%uMhz) " + "flags 0x%x hal status %u\n", __func__, + ath9k_hw_mhz2ieee(ah, hchan->channel, + hchan->channelFlags), + hchan->channel, hchan->channelFlags, status); + spin_unlock_bh(&sc->sc_resetlock); + return -EIO; + } + spin_unlock_bh(&sc->sc_resetlock); + + sc->sc_curchan = *hchan; + sc->sc_update_chainmask = 0; + sc->sc_full_reset = 0; + + /* Re-enable rx framework */ + if (ath_startrecv(sc) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to restart recv logic\n", __func__); + return -EIO; + } + /* + * Change channels and update the h/w rate map + * if we're switching; e.g. 11a to 11b/g. + */ + ath_chan_change(sc, hchan); + ath_update_txpow(sc); /* update tx power state */ + /* + * Re-enable interrupts. + */ + ath9k_hw_set_interrupts(ah, sc->sc_imask); + } + return 0; +} + +/**********************/ +/* Chainmask Handling */ +/**********************/ + +static void ath_chainmask_sel_timertimeout(unsigned long data) +{ + struct ath_chainmask_sel *cm = (struct ath_chainmask_sel *)data; + cm->switch_allowed = 1; +} + +/* Start chainmask select timer */ +static void ath_chainmask_sel_timerstart(struct ath_chainmask_sel *cm) +{ + cm->switch_allowed = 0; + mod_timer(&cm->timer, ath_chainmask_sel_period); +} + +/* Stop chainmask select timer */ +static void ath_chainmask_sel_timerstop(struct ath_chainmask_sel *cm) +{ + cm->switch_allowed = 0; + del_timer_sync(&cm->timer); +} + +static void ath_chainmask_sel_init(struct ath_softc *sc, struct ath_node *an) +{ + struct ath_chainmask_sel *cm = &an->an_chainmask_sel; + + memzero(cm, sizeof(struct ath_chainmask_sel)); + + cm->cur_tx_mask = sc->sc_tx_chainmask; + cm->cur_rx_mask = sc->sc_rx_chainmask; + cm->tx_avgrssi = ATH_RSSI_DUMMY_MARKER; + setup_timer(&cm->timer, + ath_chainmask_sel_timertimeout, (unsigned long) cm); +} + +int ath_chainmask_sel_logic(struct ath_softc *sc, struct ath_node *an) +{ + struct ath_chainmask_sel *cm = &an->an_chainmask_sel; + + /* + * Disable auto-swtiching in one of the following if conditions. + * sc_chainmask_auto_sel is used for internal global auto-switching + * enabled/disabled setting + */ + if (sc->sc_ah->ah_caps.halTxChainMask != ATH_CHAINMASK_SEL_3X3) { + cm->cur_tx_mask = sc->sc_tx_chainmask; + return cm->cur_tx_mask; + } + + if (cm->tx_avgrssi == ATH_RSSI_DUMMY_MARKER) + return cm->cur_tx_mask; + + if (cm->switch_allowed) { + /* Switch down from tx 3 to tx 2. */ + if (cm->cur_tx_mask == ATH_CHAINMASK_SEL_3X3 && + ATH_RSSI_OUT(cm->tx_avgrssi) >= + ath_chainmask_sel_down_rssi_thres) { + cm->cur_tx_mask = sc->sc_tx_chainmask; + + /* Don't let another switch happen until + * this timer expires */ + ath_chainmask_sel_timerstart(cm); + } + /* Switch up from tx 2 to 3. */ + else if (cm->cur_tx_mask == sc->sc_tx_chainmask && + ATH_RSSI_OUT(cm->tx_avgrssi) <= + ath_chainmask_sel_up_rssi_thres) { + cm->cur_tx_mask = ATH_CHAINMASK_SEL_3X3; + + /* Don't let another switch happen + * until this timer expires */ + ath_chainmask_sel_timerstart(cm); + } + } + + return cm->cur_tx_mask; +} + +/* + * Update tx/rx chainmask. For legacy association, + * hard code chainmask to 1x1, for 11n association, use + * the chainmask configuration. + */ + +void ath_update_chainmask(struct ath_softc *sc, int is_ht) +{ + sc->sc_update_chainmask = 1; + if (is_ht) { + sc->sc_tx_chainmask = sc->sc_ah->ah_caps.halTxChainMask; + sc->sc_rx_chainmask = sc->sc_ah->ah_caps.halRxChainMask; + } else { + sc->sc_tx_chainmask = 1; + sc->sc_rx_chainmask = 1; + } + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: tx chmask: %d, rx chmask: %d\n", + __func__, sc->sc_tx_chainmask, sc->sc_rx_chainmask); +} + +/******************/ +/* VAP management */ +/******************/ + +/* + * VAP in Listen mode + * + * This routine brings the VAP out of the down state into a "listen" state + * where it waits for association requests. This is used in AP and AdHoc + * modes. +*/ + +int ath_vap_listen(struct ath_softc *sc, int if_id) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_vap *avp; + u32 rfilt = 0; + DECLARE_MAC_BUF(mac); + + avp = sc->sc_vaps[if_id]; + if (avp == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid interface id %u\n", + __func__, if_id); + return -EINVAL; + } + +#ifdef CONFIG_SLOW_ANT_DIV + ath_slow_ant_div_stop(&sc->sc_antdiv); +#endif + + /* update ratectrl about the new state */ + ath_rate_newstate(sc, avp); + + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); + + if (sc->sc_opmode == ATH9K_M_STA || sc->sc_opmode == ATH9K_M_IBSS) { + memcpy(sc->sc_curbssid, ath_bcast_mac, ETH_ALEN); + ath9k_hw_write_associd(ah, sc->sc_curbssid, sc->sc_curaid); + } else + sc->sc_curaid = 0; + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: RX filter 0x%x bssid %s aid 0x%x\n", + __func__, rfilt, print_mac(mac, + sc->sc_curbssid), sc->sc_curaid); + + /* + * XXXX + * Disable BMISS interrupt when we're not associated + */ + ath9k_hw_set_interrupts(ah, + sc->sc_imask & ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS)); + sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); + /* need to reconfigure the beacons when it moves to RUN */ + sc->sc_beacons = 0; + + return 0; +} + +int ath_vap_attach(struct ath_softc *sc, + int if_id, + struct ieee80211_vif *if_data, + enum ath9k_opmode opmode) +{ + struct ath_vap *avp; + + if (if_id >= ATH_BCBUF || sc->sc_vaps[if_id] != NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Invalid interface id = %u\n", __func__, if_id); + return -EINVAL; + } + + switch (opmode) { + case ATH9K_M_STA: + case ATH9K_M_IBSS: + case ATH9K_M_MONITOR: + break; + case ATH9K_M_HOSTAP: + /* XXX not right, beacon buffer is allocated on RUN trans */ + if (list_empty(&sc->sc_bbuf)) + return -ENOMEM; + break; + default: + return -EINVAL; + } + + /* create ath_vap */ + avp = kmalloc(sizeof(struct ath_vap), GFP_KERNEL); + if (avp == NULL) + return -ENOMEM; + + memzero(avp, sizeof(struct ath_vap)); + avp->av_if_data = if_data; + /* Set the VAP opmode */ + avp->av_opmode = opmode; + avp->av_bslot = -1; + INIT_LIST_HEAD(&avp->av_mcastq.axq_q); + INIT_LIST_HEAD(&avp->av_mcastq.axq_acq); + spin_lock_init(&avp->av_mcastq.axq_lock); + + ath9k_hw_set_tsfadjust(sc->sc_ah, 1); + + sc->sc_vaps[if_id] = avp; + sc->sc_nvaps++; + /* Set the device opmode */ + sc->sc_opmode = opmode; + + /* default VAP configuration */ + avp->av_config.av_fixed_rateset = IEEE80211_FIXED_RATE_NONE; + avp->av_config.av_fixed_retryset = 0x03030303; + + return 0; +} + +int ath_vap_detach(struct ath_softc *sc, int if_id) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_vap *avp; + + avp = sc->sc_vaps[if_id]; + if (avp == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid interface id %u\n", + __func__, if_id); + return -EINVAL; + } + + /* + * Quiesce the hardware while we remove the vap. In + * particular we need to reclaim all references to the + * vap state by any frames pending on the tx queues. + * + * XXX can we do this w/o affecting other vap's? + */ + ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */ + ath_draintxq(sc, false); /* stop xmit side */ + ath_stoprecv(sc); /* stop recv side */ + ath_flushrecv(sc); /* flush recv queue */ + + /* Reclaim any pending mcast bufs on the vap. */ + ath_tx_draintxq(sc, &avp->av_mcastq, false); + + kfree(avp); + sc->sc_vaps[if_id] = NULL; + sc->sc_nvaps--; + + return 0; +} + +int ath_vap_config(struct ath_softc *sc, + int if_id, struct ath_vap_config *if_config) +{ + struct ath_vap *avp; + + if (if_id >= ATH_BCBUF) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Invalid interface id = %u\n", __func__, if_id); + return -EINVAL; + } + + avp = sc->sc_vaps[if_id]; + ASSERT(avp != NULL); + + if (avp) + memcpy(&avp->av_config, if_config, sizeof(avp->av_config)); + + return 0; +} + +/********/ +/* Core */ +/********/ + +int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan) +{ + struct ath_hal *ah = sc->sc_ah; + int status; + int error = 0; + enum ath9k_ht_macmode ht_macmode = ath_cwm_macmode(sc); + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: mode %d\n", __func__, sc->sc_opmode); + + /* + * Stop anything previously setup. This is safe + * whether this is the first time through or not. + */ + ath_stop(sc); + + /* Initialize chanmask selection */ + sc->sc_tx_chainmask = ah->ah_caps.halTxChainMask; + sc->sc_rx_chainmask = ah->ah_caps.halRxChainMask; + + /* Reset SERDES registers */ + ath9k_hw_configpcipowersave(ah, 0); + + /* + * The basic interface to setting the hardware in a good + * state is ``reset''. On return the hardware is known to + * be powered up and with interrupts disabled. This must + * be followed by initialization of the appropriate bits + * and then setup of the interrupt mask. + */ + sc->sc_curchan = *initial_chan; + + spin_lock_bh(&sc->sc_resetlock); + if (!ath9k_hw_reset(ah, sc->sc_opmode, &sc->sc_curchan, ht_macmode, + sc->sc_tx_chainmask, sc->sc_rx_chainmask, + sc->sc_ht_extprotspacing, false, &status)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to reset hardware; hal status %u " + "(freq %u flags 0x%x)\n", __func__, status, + sc->sc_curchan.channel, sc->sc_curchan.channelFlags); + error = -EIO; + spin_unlock_bh(&sc->sc_resetlock); + goto done; + } + spin_unlock_bh(&sc->sc_resetlock); + /* + * This is needed only to setup initial state + * but it's best done after a reset. + */ + ath_update_txpow(sc); + + /* + * Setup the hardware after reset: + * The receive engine is set going. + * Frame transmit is handled entirely + * in the frame output path; there's nothing to do + * here except setup the interrupt mask. + */ + if (ath_startrecv(sc) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to start recv logic\n", __func__); + error = -EIO; + goto done; + } + /* Setup our intr mask. */ + sc->sc_imask = ATH9K_INT_RX | ATH9K_INT_TX + | ATH9K_INT_RXEOL | ATH9K_INT_RXORN + | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL; + + if (ah->ah_caps.halGTTSupport) + sc->sc_imask |= ATH9K_INT_GTT; + + if (ah->ah_caps.halHTSupport) + sc->sc_imask |= ATH9K_INT_CST; + + /* + * Enable MIB interrupts when there are hardware phy counters. + * Note we only do this (at the moment) for station mode. + */ + if (ath9k_hw_phycounters(ah) && + ((sc->sc_opmode == ATH9K_M_STA) || (sc->sc_opmode == ATH9K_M_IBSS))) + sc->sc_imask |= ATH9K_INT_MIB; + /* + * Some hardware processes the TIM IE and fires an + * interrupt when the TIM bit is set. For hardware + * that does, if not overridden by configuration, + * enable the TIM interrupt when operating as station. + */ + if (ah->ah_caps.halEnhancedPmSupport && sc->sc_opmode == ATH9K_M_STA && + !sc->sc_config.swBeaconProcess) + sc->sc_imask |= ATH9K_INT_TIM; + /* + * Don't enable interrupts here as we've not yet built our + * vap and node data structures, which will be needed as soon + * as we start receiving. + */ + ath_chan_change(sc, initial_chan); + + /* XXX: we must make sure h/w is ready and clear invalid flag + * before turning on interrupt. */ + sc->sc_invalid = 0; +done: + return error; +} + +/* + * Reset the hardware w/o losing operational state. This is + * basically a more efficient way of doing ath_stop, ath_init, + * followed by state transitions to the current 802.11 + * operational state. Used to recover from errors rx overrun + * and to reset the hardware when rf gain settings must be reset. + */ + +static int ath_reset_start(struct ath_softc *sc, u32 flag) +{ + struct ath_hal *ah = sc->sc_ah; + + ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */ + ath_draintxq(sc, flag & RESET_RETRY_TXQ); /* stop xmit side */ + ath_stoprecv(sc); /* stop recv side */ + ath_flushrecv(sc); /* flush recv queue */ + + return 0; +} + +static int ath_reset_end(struct ath_softc *sc, u32 flag) +{ + struct ath_hal *ah = sc->sc_ah; + + if (ath_startrecv(sc) != 0) /* restart recv */ + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to start recv logic\n", __func__); + + /* + * We may be doing a reset in response to a request + * that changes the channel so update any state that + * might change as a result. + */ + ath_chan_change(sc, &sc->sc_curchan); + + ath_update_txpow(sc); /* update tx power state */ + + if (sc->sc_beacons) + ath_beacon_config(sc, ATH_IF_ID_ANY); /* restart beacons */ + ath9k_hw_set_interrupts(ah, sc->sc_imask); + + /* Restart the txq */ + if (flag & RESET_RETRY_TXQ) { + int i; + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + spin_lock_bh(&sc->sc_txq[i].axq_lock); + ath_txq_schedule(sc, &sc->sc_txq[i]); + spin_unlock_bh(&sc->sc_txq[i].axq_lock); + } + } + } + return 0; +} + +int ath_reset(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + int status; + int error = 0; + enum ath9k_ht_macmode ht_macmode = ath_cwm_macmode(sc); + + /* NB: indicate channel change so we do a full reset */ + spin_lock_bh(&sc->sc_resetlock); + if (!ath9k_hw_reset(ah, sc->sc_opmode, &sc->sc_curchan, + ht_macmode, + sc->sc_tx_chainmask, sc->sc_rx_chainmask, + sc->sc_ht_extprotspacing, false, &status)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to reset hardware; hal status %u\n", + __func__, status); + error = -EIO; + } + spin_unlock_bh(&sc->sc_resetlock); + + return error; +} + +int ath_suspend(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + + /* No I/O if device has been surprise removed */ + if (sc->sc_invalid) + return -EIO; + + /* Shut off the interrupt before setting sc->sc_invalid to '1' */ + ath9k_hw_set_interrupts(ah, 0); + + /* XXX: we must make sure h/w will not generate any interrupt + * before setting the invalid flag. */ + sc->sc_invalid = 1; + + /* disable HAL and put h/w to sleep */ + ath9k_hw_disable(sc->sc_ah); + + ath9k_hw_configpcipowersave(sc->sc_ah, 1); + + return 0; +} + +/* Interrupt handler. Most of the actual processing is deferred. + * It's the caller's responsibility to ensure the chip is awake. */ + +irqreturn_t ath_isr(int irq, void *dev) +{ + struct ath_softc *sc = dev; + struct ath_hal *ah = sc->sc_ah; + enum ath9k_int status; + bool sched = false; + + do { + if (sc->sc_invalid) { + /* + * The hardware is not ready/present, don't + * touch anything. Note this can happen early + * on if the IRQ is shared. + */ + return IRQ_NONE; + } + if (!ath9k_hw_intrpend(ah)) { /* shared irq, not for us */ + return IRQ_NONE; + } + + /* + * Figure out the reason(s) for the interrupt. Note + * that the hal returns a pseudo-ISR that may include + * bits we haven't explicitly enabled so we mask the + * value to insure we only process bits we requested. + */ + ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */ + + status &= sc->sc_imask; /* discard unasked-for bits */ + + /* + * If there are no status bits set, then this interrupt was not + * for me (should have been caught above). + */ + + if (!status) + return IRQ_NONE; + + sc->sc_intrstatus = status; + + if (status & ATH9K_INT_FATAL) { + /* need a chip reset */ + sched = true; + } else if (status & ATH9K_INT_RXORN) { + /* need a chip reset */ + sched = true; + } else { + if (status & ATH9K_INT_SWBA) { + /* schedule a tasklet for beacon handling */ + tasklet_schedule(&sc->bcon_tasklet); + } + if (status & ATH9K_INT_RXEOL) { + /* + * NB: the hardware should re-read the link when + * RXE bit is written, but it doesn't work + * at least on older hardware revs. + */ + sched = true; + } + + if (status & ATH9K_INT_TXURN) + /* bump tx trigger level */ + ath9k_hw_updatetxtriglevel(ah, true); + /* XXX: optimize this */ + if (status & ATH9K_INT_RX) + sched = true; + if (status & ATH9K_INT_TX) + sched = true; + if (status & ATH9K_INT_BMISS) + sched = true; + /* carrier sense timeout */ + if (status & ATH9K_INT_CST) + sched = true; + if (status & ATH9K_INT_MIB) { + /* + * Disable interrupts until we service the MIB + * interrupt; otherwise it will continue to + * fire. + */ + ath9k_hw_set_interrupts(ah, 0); + /* + * Let the hal handle the event. We assume + * it will clear whatever condition caused + * the interrupt. + */ + ath9k_hw_procmibevent(ah, &sc->sc_halstats); + ath9k_hw_set_interrupts(ah, sc->sc_imask); + } + if (status & ATH9K_INT_TIM_TIMER) { + if (!ah->ah_caps.halAutoSleepSupport) { + /* Clear RxAbort bit so that we can + * receive frames */ + ath9k_hw_setrxabort(ah, 0); + sched = true; + } + } + } + } while (0); + + if (sched) { + /* turn off every interrupt except SWBA */ + ath9k_hw_set_interrupts(ah, (sc->sc_imask & ATH9K_INT_SWBA)); + tasklet_schedule(&sc->intr_tq); + } + + return IRQ_HANDLED; +} + +/* Deferred interrupt processing */ + +static void ath9k_tasklet(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + u32 status = sc->sc_intrstatus; + + if (status & ATH9K_INT_FATAL) { + /* need a chip reset */ + ath_internal_reset(sc); + return; + } else { + + if (status & + (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN)) { + /* XXX: fill me in */ + /* + if (status & ATH9K_INT_RXORN) { + } + if (status & ATH9K_INT_RXEOL) { + } + */ + spin_lock_bh(&sc->sc_rxflushlock); + ath_rx_tasklet(sc, 0); + spin_unlock_bh(&sc->sc_rxflushlock); + } + /* XXX: optimize this */ + if (status & ATH9K_INT_TX) + ath_tx_tasklet(sc); + /* XXX: fill me in */ + /* + if (status & ATH9K_INT_BMISS) { + } + if (status & (ATH9K_INT_TIM | ATH9K_INT_DTIMSYNC)) { + if (status & ATH9K_INT_TIM) { + } + if (status & ATH9K_INT_DTIMSYNC) { + } + } + */ + } + + /* re-enable hardware interrupt */ + ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask); +} + +int ath_init(u16 devid, struct ath_softc *sc) +{ + struct ath_hal *ah = NULL; + int status; + int error = 0, i; + int csz = 0; + u32 rd; + + /* XXX: hardware will not be ready until ath_open() being called */ + sc->sc_invalid = 1; + + sc->sc_debug = DBG_DEFAULT; + DPRINTF(sc, ATH_DBG_CONFIG, "%s: devid 0x%x\n", __func__, devid); + + /* Initialize tasklet */ + tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); + tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, + (unsigned long)sc); + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + bus_read_cachesize(sc, &csz); + /* XXX assert csz is non-zero */ + sc->sc_cachelsz = csz << 2; /* convert to bytes */ + + spin_lock_init(&sc->sc_resetlock); + + ah = ath9k_hw_attach(devid, sc, sc->mem, &status); + if (ah == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to attach hardware; HAL status %u\n", + __func__, status); + error = -ENXIO; + goto bad; + } + sc->sc_ah = ah; + + /* Get the chipset-specific aggr limit. */ + sc->sc_rtsaggrlimit = ah->ah_caps.halRtsAggrLimit; + + /* Get the hardware key cache size. */ + sc->sc_keymax = ah->ah_caps.halKeyCacheSize; + if (sc->sc_keymax > ATH_KEYMAX) { + DPRINTF(sc, ATH_DBG_KEYCACHE, + "%s: Warning, using only %u entries in %u key cache\n", + __func__, ATH_KEYMAX, sc->sc_keymax); + sc->sc_keymax = ATH_KEYMAX; + } + + /* + * Reset the key cache since some parts do not + * reset the contents on initial power up. + */ + for (i = 0; i < sc->sc_keymax; i++) + ath9k_hw_keyreset(ah, (u16) i); + /* + * Mark key cache slots associated with global keys + * as in use. If we knew TKIP was not to be used we + * could leave the +32, +64, and +32+64 slots free. + * XXX only for splitmic. + */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + set_bit(i, sc->sc_keymap); + set_bit(i + 32, sc->sc_keymap); + set_bit(i + 64, sc->sc_keymap); + set_bit(i + 32 + 64, sc->sc_keymap); + } + /* + * Collect the channel list using the default country + * code and including outdoor channels. The 802.11 layer + * is resposible for filtering this list based on settings + * like the phy mode. + */ + rd = ah->ah_currentRD; + + error = ath_setup_channels(sc); + if (error) + goto bad; + + /* default to STA mode */ + sc->sc_opmode = ATH9K_M_MONITOR; + + /* Setup rate tables for all potential media types. */ + /* 11g encompasses b,g */ + + ath_rate_setup(sc, WIRELESS_MODE_11a); + ath_rate_setup(sc, WIRELESS_MODE_11g); + + /* NB: setup here so ath_rate_update is happy */ + ath_setcurmode(sc, WIRELESS_MODE_11a); + + /* + * Allocate hardware transmit queues: one queue for + * beacon frames and one data queue for each QoS + * priority. Note that the hal handles reseting + * these queues at the needed time. + */ + sc->sc_bhalq = ath_beaconq_setup(ah); + if (sc->sc_bhalq == -1) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup a beacon xmit queue\n", __func__); + error = -EIO; + goto bad2; + } + sc->sc_cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); + if (sc->sc_cabq == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup CAB xmit queue\n", __func__); + error = -EIO; + goto bad2; + } + + sc->sc_config.cabqReadytime = ATH_CABQ_READY_TIME; + ath_cabq_update(sc); + + for (i = 0; i < ARRAY_SIZE(sc->sc_haltype2q); i++) + sc->sc_haltype2q[i] = -1; + + /* Setup data queues */ + /* NB: ensure BK queue is the lowest priority h/w queue */ + if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup xmit queue for BK traffic\n", + __func__); + error = -EIO; + goto bad2; + } + + if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup xmit queue for BE traffic\n", + __func__); + error = -EIO; + goto bad2; + } + if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup xmit queue for VI traffic\n", + __func__); + error = -EIO; + goto bad2; + } + if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup xmit queue for VO traffic\n", + __func__); + error = -EIO; + goto bad2; + } + + sc->sc_rc = ath_rate_attach(ah); + if (sc->sc_rc == NULL) { + error = EIO; + goto bad2; + } + + if (ath9k_hw_getcapability(ah, HAL_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL)) { + /* + * Whether we should enable h/w TKIP MIC. + * XXX: if we don't support WME TKIP MIC, then we wouldn't + * report WMM capable, so it's always safe to turn on + * TKIP MIC in this case. + */ + ath9k_hw_setcapability(sc->sc_ah, HAL_CAP_TKIP_MIC, 0, 1, NULL); + } + + /* + * Check whether the separate key cache entries + * are required to handle both tx+rx MIC keys. + * With split mic keys the number of stations is limited + * to 27 otherwise 59. + */ + if (ath9k_hw_getcapability(ah, HAL_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL) + && ath9k_hw_getcapability(ah, HAL_CAP_CIPHER, + ATH9K_CIPHER_MIC, NULL) + && ath9k_hw_getcapability(ah, HAL_CAP_TKIP_SPLIT, + 0, NULL)) + sc->sc_splitmic = 1; + + /* turn on mcast key search if possible */ + if (!ath9k_hw_getcapability(ah, HAL_CAP_MCAST_KEYSRCH, 0, NULL)) + (void)ath9k_hw_setcapability(ah, HAL_CAP_MCAST_KEYSRCH, 1, + 1, NULL); + + sc->sc_config.txpowlimit = ATH_TXPOWER_MAX; + sc->sc_config.txpowlimit_override = 0; + + /* 11n Capabilities */ + if (ah->ah_caps.halHTSupport) { + sc->sc_txaggr = 1; + sc->sc_rxaggr = 1; + } + + sc->sc_tx_chainmask = ah->ah_caps.halTxChainMask; + sc->sc_rx_chainmask = ah->ah_caps.halRxChainMask; + + /* Configuration for rx chain detection */ + sc->sc_rxchaindetect_ref = 0; + sc->sc_rxchaindetect_thresh5GHz = 35; + sc->sc_rxchaindetect_thresh2GHz = 35; + sc->sc_rxchaindetect_delta5GHz = 30; + sc->sc_rxchaindetect_delta2GHz = 30; + + ath9k_hw_setcapability(ah, HAL_CAP_DIVERSITY, 1, true, NULL); + sc->sc_defant = ath9k_hw_getdefantenna(ah); + + ath9k_hw_getmac(ah, sc->sc_myaddr); + if (ah->ah_caps.halBssIdMaskSupport) { + ath9k_hw_getbssidmask(ah, sc->sc_bssidmask); + ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask); + ath9k_hw_setbssidmask(ah, sc->sc_bssidmask); + } + sc->sc_slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */ + + /* initialize beacon slots */ + for (i = 0; i < ARRAY_SIZE(sc->sc_bslot); i++) + sc->sc_bslot[i] = ATH_IF_ID_ANY; + + /* save MISC configurations */ + sc->sc_config.swBeaconProcess = 1; + +#ifdef CONFIG_SLOW_ANT_DIV + /* range is 40 - 255, we use something in the middle */ + ath_slow_ant_div_init(&sc->sc_antdiv, sc, 0x127); +#endif + + return 0; +bad2: + /* cleanup tx queues */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->sc_txq[i]); +bad: + if (ah) + ath9k_hw_detach(ah); + return error; +} + +void ath_deinit(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + int i; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s\n", __func__); + + ath_stop(sc); + if (!sc->sc_invalid) + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + ath_rate_detach(sc->sc_rc); + /* cleanup tx queues */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->sc_txq[i]); + ath9k_hw_detach(ah); +} + +/*******************/ +/* Node Management */ +/*******************/ + +struct ath_node *ath_node_attach(struct ath_softc *sc, u8 *addr, int if_id) +{ + struct ath_vap *avp; + struct ath_node *an; + DECLARE_MAC_BUF(mac); + + avp = sc->sc_vaps[if_id]; + ASSERT(avp != NULL); + + /* mac80211 sta_notify callback is from an IRQ context, so no sleep */ + an = kmalloc(sizeof(struct ath_node), GFP_ATOMIC); + if (an == NULL) + return NULL; + memzero(an, sizeof(*an)); + + an->an_sc = sc; + memcpy(an->an_addr, addr, ETH_ALEN); + atomic_set(&an->an_refcnt, 1); + + /* set up per-node tx/rx state */ + ath_tx_node_init(sc, an); + ath_rx_node_init(sc, an); + + ath_chainmask_sel_init(sc, an); + ath_chainmask_sel_timerstart(&an->an_chainmask_sel); + list_add(&an->list, &sc->node_list); + + return an; +} + +void ath_node_detach(struct ath_softc *sc, struct ath_node *an, bool bh_flag) +{ + unsigned long flags; + + DECLARE_MAC_BUF(mac); + + ath_chainmask_sel_timerstop(&an->an_chainmask_sel); + an->an_flags |= ATH_NODE_CLEAN; + ath_tx_node_cleanup(sc, an, bh_flag); + ath_rx_node_cleanup(sc, an); + + ath_tx_node_free(sc, an); + ath_rx_node_free(sc, an); + + spin_lock_irqsave(&sc->node_lock, flags); + + list_del(&an->list); + + spin_unlock_irqrestore(&sc->node_lock, flags); + + kfree(an); +} + +/* Finds a node and increases the refcnt if found */ + +struct ath_node *ath_node_get(struct ath_softc *sc, u8 *addr) +{ + struct ath_node *an = NULL, *an_found = NULL; + + if (list_empty(&sc->node_list)) /* FIXME */ + goto out; + list_for_each_entry(an, &sc->node_list, list) { + if (!compare_ether_addr(an->an_addr, addr)) { + atomic_inc(&an->an_refcnt); + an_found = an; + break; + } + } +out: + return an_found; +} + +/* Decrements the refcnt and if it drops to zero, detach the node */ + +void ath_node_put(struct ath_softc *sc, struct ath_node *an, bool bh_flag) +{ + if (atomic_dec_and_test(&an->an_refcnt)) + ath_node_detach(sc, an, bh_flag); +} + +/* Finds a node, doesn't increment refcnt. Caller must hold sc->node_lock */ +struct ath_node *ath_node_find(struct ath_softc *sc, u8 *addr) +{ + struct ath_node *an = NULL, *an_found = NULL; + + if (list_empty(&sc->node_list)) + return NULL; + + list_for_each_entry(an, &sc->node_list, list) + if (!compare_ether_addr(an->an_addr, addr)) { + an_found = an; + break; + } + + return an_found; +} + +/* + * Set up New Node + * + * Setup driver-specific state for a newly associated node. This routine + * really only applies if compression or XR are enabled, there is no code + * covering any other cases. +*/ + +void ath_newassoc(struct ath_softc *sc, + struct ath_node *an, int isnew, int isuapsd) +{ + int tidno; + + /* if station reassociates, tear down the aggregation state. */ + if (!isnew) { + for (tidno = 0; tidno < WME_NUM_TID; tidno++) { + if (sc->sc_txaggr) + ath_tx_aggr_teardown(sc, an, tidno); + if (sc->sc_rxaggr) + ath_rx_aggr_teardown(sc, an, tidno); + } + } + an->an_flags = 0; +} + +/**************/ +/* Encryption */ +/**************/ + +void ath_key_reset(struct ath_softc *sc, u16 keyix, int freeslot) +{ + ath9k_hw_keyreset(sc->sc_ah, keyix); + if (freeslot) + clear_bit(keyix, sc->sc_keymap); +} + +int ath_keyset(struct ath_softc *sc, + u16 keyix, + struct ath9k_keyval *hk, + const u8 mac[ETH_ALEN]) +{ + bool status; + + status = ath9k_hw_set_keycache_entry(sc->sc_ah, + keyix, hk, mac, false); + + return status != false; +} + +/***********************/ +/* TX Power/Regulatory */ +/***********************/ + +/* + * Set Transmit power in HAL + * + * This routine makes the actual HAL calls to set the new transmit power + * limit. +*/ + +void ath_update_txpow(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + u32 txpow; + + if (sc->sc_curtxpow != sc->sc_config.txpowlimit) { + ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit); + /* read back in case value is clamped */ + ath9k_hw_getcapability(ah, HAL_CAP_TXPOW, 1, &txpow); + sc->sc_curtxpow = txpow; + } +} + +/* Return the current country and domain information */ +void ath_get_currentCountry(struct ath_softc *sc, + struct ath9k_country_entry *ctry) +{ + ath9k_regd_get_current_country(sc->sc_ah, ctry); + + /* If HAL not specific yet, since it is band dependent, + * use the one we passed in. */ + if (ctry->countryCode == CTRY_DEFAULT) { + ctry->iso[0] = 0; + ctry->iso[1] = 0; + } else if (ctry->iso[0] && ctry->iso[1]) { + if (!ctry->iso[2]) { + if (ath_outdoor) + ctry->iso[2] = 'O'; + else + ctry->iso[2] = 'I'; + } + } +} + +/**************************/ +/* Slow Antenna Diversity */ +/**************************/ + +void ath_slow_ant_div_init(struct ath_antdiv *antdiv, + struct ath_softc *sc, + int32_t rssitrig) +{ + int trig; + + /* antdivf_rssitrig can range from 40 - 0xff */ + trig = (rssitrig > 0xff) ? 0xff : rssitrig; + trig = (rssitrig < 40) ? 40 : rssitrig; + + antdiv->antdiv_sc = sc; + antdiv->antdivf_rssitrig = trig; +} + +void ath_slow_ant_div_start(struct ath_antdiv *antdiv, + u8 num_antcfg, + const u8 *bssid) +{ + antdiv->antdiv_num_antcfg = + num_antcfg < ATH_ANT_DIV_MAX_CFG ? + num_antcfg : ATH_ANT_DIV_MAX_CFG; + antdiv->antdiv_state = ATH_ANT_DIV_IDLE; + antdiv->antdiv_curcfg = 0; + antdiv->antdiv_bestcfg = 0; + antdiv->antdiv_laststatetsf = 0; + + memcpy(antdiv->antdiv_bssid, bssid, sizeof(antdiv->antdiv_bssid)); + + antdiv->antdiv_start = 1; +} + +void ath_slow_ant_div_stop(struct ath_antdiv *antdiv) +{ + antdiv->antdiv_start = 0; +} + +static int32_t ath_find_max_val(int32_t *val, + u8 num_val, u8 *max_index) +{ + u32 MaxVal = *val++; + u32 cur_index = 0; + + *max_index = 0; + while (++cur_index < num_val) { + if (*val > MaxVal) { + MaxVal = *val; + *max_index = cur_index; + } + + val++; + } + + return MaxVal; +} + +void ath_slow_ant_div(struct ath_antdiv *antdiv, + struct ieee80211_hdr *hdr, + struct ath_rx_status *rx_stats) +{ + struct ath_softc *sc = antdiv->antdiv_sc; + struct ath_hal *ah = sc->sc_ah; + u64 curtsf = 0; + u8 bestcfg, curcfg = antdiv->antdiv_curcfg; + __le16 fc = hdr->frame_control; + + if (antdiv->antdiv_start && ieee80211_is_beacon(fc) + && !compare_ether_addr(hdr->addr3, antdiv->antdiv_bssid)) { + antdiv->antdiv_lastbrssi[curcfg] = rx_stats->rs_rssi; + antdiv->antdiv_lastbtsf[curcfg] = ath9k_hw_gettsf64(sc->sc_ah); + curtsf = antdiv->antdiv_lastbtsf[curcfg]; + } else { + return; + } + + switch (antdiv->antdiv_state) { + case ATH_ANT_DIV_IDLE: + if ((antdiv->antdiv_lastbrssi[curcfg] < + antdiv->antdivf_rssitrig) + && ((curtsf - antdiv->antdiv_laststatetsf) > + ATH_ANT_DIV_MIN_IDLE_US)) { + + curcfg++; + if (curcfg == antdiv->antdiv_num_antcfg) + curcfg = 0; + + if (!ath9k_hw_select_antconfig(ah, curcfg)) { + antdiv->antdiv_bestcfg = antdiv->antdiv_curcfg; + antdiv->antdiv_curcfg = curcfg; + antdiv->antdiv_laststatetsf = curtsf; + antdiv->antdiv_state = ATH_ANT_DIV_SCAN; + } + } + break; + + case ATH_ANT_DIV_SCAN: + if ((curtsf - antdiv->antdiv_laststatetsf) < + ATH_ANT_DIV_MIN_SCAN_US) + break; + + curcfg++; + if (curcfg == antdiv->antdiv_num_antcfg) + curcfg = 0; + + if (curcfg == antdiv->antdiv_bestcfg) { + ath_find_max_val(antdiv->antdiv_lastbrssi, + antdiv->antdiv_num_antcfg, &bestcfg); + if (!ath9k_hw_select_antconfig(ah, bestcfg)) { + antdiv->antdiv_bestcfg = bestcfg; + antdiv->antdiv_curcfg = bestcfg; + antdiv->antdiv_laststatetsf = curtsf; + antdiv->antdiv_state = ATH_ANT_DIV_IDLE; + } + } else { + if (!ath9k_hw_select_antconfig(ah, curcfg)) { + antdiv->antdiv_curcfg = curcfg; + antdiv->antdiv_laststatetsf = curtsf; + antdiv->antdiv_state = ATH_ANT_DIV_SCAN; + } + } + + break; + } +} + +/***********************/ +/* Descriptor Handling */ +/***********************/ + +/* + * Set up DMA descriptors + * + * This function will allocate both the DMA descriptor structure, and the + * buffers it contains. These are used to contain the descriptors used + * by the system. +*/ + +int ath_descdma_setup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head, + const char *name, + int nbuf, + int ndesc) +{ +#define DS2PHYS(_dd, _ds) \ + ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) +#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) +#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096) + + struct ath_desc *ds; + struct ath_buf *bf; + int i, bsize, error; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: %s DMA: %u buffers %u desc/buf\n", + __func__, name, nbuf, ndesc); + + /* ath_desc must be a multiple of DWORDs */ + if ((sizeof(struct ath_desc) % 4) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: ath_desc not DWORD aligned\n", + __func__); + ASSERT((sizeof(struct ath_desc) % 4) == 0); + error = -ENOMEM; + goto fail; + } + + dd->dd_name = name; + dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc; + + /* + * Need additional DMA memory because we can't use + * descriptors that cross the 4K page boundary. Assume + * one skipped descriptor per 4K page. + */ + if (!(sc->sc_ah->ah_caps.hal4kbSplitTransSupport)) { + u32 ndesc_skipped = + ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len); + u32 dma_len; + + while (ndesc_skipped) { + dma_len = ndesc_skipped * sizeof(struct ath_desc); + dd->dd_desc_len += dma_len; + + ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len); + }; + } + + /* allocate descriptors */ + dd->dd_desc = pci_alloc_consistent(sc->pdev, + dd->dd_desc_len, + &dd->dd_desc_paddr); + if (dd->dd_desc == NULL) { + error = -ENOMEM; + goto fail; + } + ds = dd->dd_desc; + DPRINTF(sc, ATH_DBG_CONFIG, "%s: %s DMA map: %p (%u) -> %llx (%u)\n", + __func__, dd->dd_name, ds, (u32) dd->dd_desc_len, + ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); + + /* allocate buffers */ + bsize = sizeof(struct ath_buf) * nbuf; + bf = kmalloc(bsize, GFP_KERNEL); + if (bf == NULL) { + error = -ENOMEM; + goto fail2; + } + memzero(bf, bsize); + dd->dd_bufptr = bf; + + INIT_LIST_HEAD(head); + for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + + if (!(sc->sc_ah->ah_caps.hal4kbSplitTransSupport)) { + /* + * Skip descriptor addresses which can cause 4KB + * boundary crossing (addr + length) with a 32 dword + * descriptor fetch. + */ + while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { + ASSERT((caddr_t) bf->bf_desc < + ((caddr_t) dd->dd_desc + + dd->dd_desc_len)); + + ds += ndesc; + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + } + } + list_add_tail(&bf->list, head); + } + return 0; +fail2: + pci_free_consistent(sc->pdev, + dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr); +fail: + memzero(dd, sizeof(*dd)); + return error; +#undef ATH_DESC_4KB_BOUND_CHECK +#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED +#undef DS2PHYS +} + +/* + * Cleanup DMA descriptors + * + * This function will free the DMA block that was allocated for the descriptor + * pool. Since this was allocated as one "chunk", it is freed in the same + * manner. +*/ + +void ath_descdma_cleanup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head) +{ + /* Free memory associated with descriptors */ + pci_free_consistent(sc->pdev, + dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr); + + INIT_LIST_HEAD(head); + kfree(dd->dd_bufptr); + memzero(dd, sizeof(*dd)); +} + +/*************/ +/* Utilities */ +/*************/ + +void ath_internal_reset(struct ath_softc *sc) +{ + ath_reset_start(sc, 0); + ath_reset(sc); + ath_reset_end(sc, 0); +} + +int ath_get_hal_qnum(u16 queue, struct ath_softc *sc) +{ + int qnum; + + switch (queue) { + case 0: + qnum = sc->sc_haltype2q[ATH9K_WME_AC_VO]; + break; + case 1: + qnum = sc->sc_haltype2q[ATH9K_WME_AC_VI]; + break; + case 2: + qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE]; + break; + case 3: + qnum = sc->sc_haltype2q[ATH9K_WME_AC_BK]; + break; + default: + qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE]; + break; + } + + return qnum; +} + +int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc) +{ + int qnum; + + switch (queue) { + case ATH9K_WME_AC_VO: + qnum = 0; + break; + case ATH9K_WME_AC_VI: + qnum = 1; + break; + case ATH9K_WME_AC_BE: + qnum = 2; + break; + case ATH9K_WME_AC_BK: + qnum = 3; + break; + default: + qnum = -1; + break; + } + + return qnum; +} + + +/* + * Expand time stamp to TSF + * + * Extend 15-bit time stamp from rx descriptor to + * a full 64-bit TSF using the current h/w TSF. +*/ + +u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp) +{ + u64 tsf; + + tsf = ath9k_hw_gettsf64(sc->sc_ah); + if ((tsf & 0x7fff) < rstamp) + tsf -= 0x8000; + return (tsf & ~0x7fff) | rstamp; +} + +/* + * Set Default Antenna + * + * Call into the HAL to set the default antenna to use. Not really valid for + * MIMO technology. +*/ + +void ath_setdefantenna(void *context, u32 antenna) +{ + struct ath_softc *sc = (struct ath_softc *)context; + struct ath_hal *ah = sc->sc_ah; + + /* XXX block beacon interrupts */ + ath9k_hw_setantenna(ah, antenna); + sc->sc_defant = antenna; + sc->sc_rxotherant = 0; +} + +/* + * Set Slot Time + * + * This will wake up the chip if required, and set the slot time for the + * frame (maximum transmit time). Slot time is assumed to be already set + * in the ATH object member sc_slottime +*/ + +void ath_setslottime(struct ath_softc *sc) +{ + ath9k_hw_setslottime(sc->sc_ah, sc->sc_slottime); + sc->sc_updateslot = OK; +} diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h new file mode 100644 index 0000000..cf76b36 --- /dev/null +++ b/drivers/net/wireless/ath9k/core.h @@ -0,0 +1,1097 @@ +/* + * 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. + */ + +#ifndef CORE_H +#define CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ath9k.h" +#include "rc.h" + +struct ath_node; + +/******************/ +/* Utility macros */ +/******************/ + +/* Macro to expand scalars to 64-bit objects */ + +#define ito64(x) (sizeof(x) == 8) ? \ + (((unsigned long long int)(x)) & (0xff)) : \ + (sizeof(x) == 16) ? \ + (((unsigned long long int)(x)) & 0xffff) : \ + ((sizeof(x) == 32) ? \ + (((unsigned long long int)(x)) & 0xffffffff) : \ + (unsigned long long int)(x)) + +/* increment with wrap-around */ +#define INCR(_l, _sz) do { \ + (_l)++; \ + (_l) &= ((_sz) - 1); \ + } while (0) + +/* decrement with wrap-around */ +#define DECR(_l, _sz) do { \ + (_l)--; \ + (_l) &= ((_sz) - 1); \ + } while (0) + +#define A_MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define ASSERT(exp) do { \ + if (unlikely(!(exp))) { \ + BUG(); \ + } \ + } while (0) + +/* XXX: remove */ +#define memzero(_buf, _len) memset(_buf, 0, _len) + +#define get_dma_mem_context(var, field) (&((var)->field)) +#define copy_dma_mem_context(dst, src) (*dst = *src) + +#define ATH9K_BH_STATUS_INTACT 0 +#define ATH9K_BH_STATUS_CHANGE 1 + +#define ATH_TXQ_SETUP(sc, i) ((sc)->sc_txqsetup & (1<sc_debug & (_m)) \ + printk(_fmt , ##__VA_ARGS__); \ + } while (0) + +/***************************/ +/* Load-time Configuration */ +/***************************/ + +/* Per-instance load-time (note: NOT run-time) configurations + * for Atheros Device */ +struct ath_config { + u32 ath_aggr_prot; + u16 txpowlimit; + u16 txpowlimit_override; + u8 cabqReadytime; /* Cabq Readytime % */ + u8 swBeaconProcess; /* Process received beacons + in SW (vs HW) */ +}; + +/***********************/ +/* Chainmask Selection */ +/***********************/ + +#define ATH_CHAINMASK_SEL_TIMEOUT 6000 +/* Default - Number of last RSSI values that is used for + * chainmask selection */ +#define ATH_CHAINMASK_SEL_RSSI_CNT 10 +/* Means use 3x3 chainmask instead of configured chainmask */ +#define ATH_CHAINMASK_SEL_3X3 7 +/* Default - Rssi threshold below which we have to switch to 3x3 */ +#define ATH_CHAINMASK_SEL_UP_RSSI_THRES 20 +/* Default - Rssi threshold above which we have to switch to + * user configured values */ +#define ATH_CHAINMASK_SEL_DOWN_RSSI_THRES 35 +/* Struct to store the chainmask select related info */ +struct ath_chainmask_sel { + struct timer_list timer; + int cur_tx_mask; /* user configured or 3x3 */ + int cur_rx_mask; /* user configured or 3x3 */ + int tx_avgrssi; + u8 switch_allowed:1, /* timer will set this */ + cm_sel_enabled:1; +}; + +int ath_chainmask_sel_logic(struct ath_softc *sc, struct ath_node *an); +void ath_update_chainmask(struct ath_softc *sc, int is_ht); + +/*************************/ +/* Descriptor Management */ +/*************************/ + +/* Number of descriptors per buffer. The only case where we see skbuff +chains is due to FF aggregation in the driver. */ +#define ATH_TXDESC 1 +/* if there's more fragment for this MSDU */ +#define ATH_BF_MORE_MPDU 1 +#define ATH_TXBUF_RESET(_bf) do { \ + (_bf)->bf_status = 0; \ + (_bf)->bf_lastbf = NULL; \ + (_bf)->bf_lastfrm = NULL; \ + (_bf)->bf_next = NULL; \ + memzero(&((_bf)->bf_state), \ + sizeof(struct ath_buf_state)); \ + } while (0) + +struct ath_buf_state { + int bfs_nframes; /* # frames in aggregate */ + u16 bfs_al; /* length of aggregate */ + u16 bfs_frmlen; /* length of frame */ + int bfs_seqno; /* sequence number */ + int bfs_tidno; /* tid of this frame */ + int bfs_retries; /* current retries */ + struct ath_rc_series bfs_rcs[4]; /* rate series */ + u8 bfs_isdata:1; /* is a data frame/aggregate */ + u8 bfs_isaggr:1; /* is an aggregate */ + u8 bfs_isampdu:1; /* is an a-mpdu, aggregate or not */ + u8 bfs_ht:1; /* is an HT frame */ + u8 bfs_isretried:1; /* is retried */ + u8 bfs_isxretried:1; /* is excessive retried */ + u8 bfs_shpreamble:1; /* is short preamble */ + u8 bfs_isbar:1; /* is a BAR */ + u8 bfs_ispspoll:1; /* is a PS-Poll */ + u8 bfs_aggrburst:1; /* is a aggr burst */ + u8 bfs_calcairtime:1; /* requests airtime be calculated + when set for tx frame */ + int bfs_rifsburst_elem; /* RIFS burst/bar */ + int bfs_nrifsubframes; /* # of elements in burst */ + /* key type use to encrypt this frame */ + enum ath9k_key_type bfs_keytype; +}; + +#define bf_nframes bf_state.bfs_nframes +#define bf_al bf_state.bfs_al +#define bf_frmlen bf_state.bfs_frmlen +#define bf_retries bf_state.bfs_retries +#define bf_seqno bf_state.bfs_seqno +#define bf_tidno bf_state.bfs_tidno +#define bf_rcs bf_state.bfs_rcs +#define bf_isdata bf_state.bfs_isdata +#define bf_isaggr bf_state.bfs_isaggr +#define bf_isampdu bf_state.bfs_isampdu +#define bf_ht bf_state.bfs_ht +#define bf_isretried bf_state.bfs_isretried +#define bf_isxretried bf_state.bfs_isxretried +#define bf_shpreamble bf_state.bfs_shpreamble +#define bf_rifsburst_elem bf_state.bfs_rifsburst_elem +#define bf_nrifsubframes bf_state.bfs_nrifsubframes +#define bf_keytype bf_state.bfs_keytype +#define bf_isbar bf_state.bfs_isbar +#define bf_ispspoll bf_state.bfs_ispspoll +#define bf_aggrburst bf_state.bfs_aggrburst +#define bf_calcairtime bf_state.bfs_calcairtime + +/* + * Abstraction of a contiguous buffer to transmit/receive. There is only + * a single hw descriptor encapsulated here. + */ + +struct ath_buf { + struct list_head list; + struct list_head *last; + struct ath_buf *bf_lastbf; /* last buf of this unit (a frame or + an aggregate) */ + struct ath_buf *bf_lastfrm; /* last buf of this frame */ + struct ath_buf *bf_next; /* next subframe in the aggregate */ + struct ath_buf *bf_rifslast; /* last buf for RIFS burst */ + void *bf_mpdu; /* enclosing frame structure */ + void *bf_node; /* pointer to the node */ + struct ath_desc *bf_desc; /* virtual addr of desc */ + dma_addr_t bf_daddr; /* physical addr of desc */ + dma_addr_t bf_buf_addr; /* physical addr of data buffer */ + u32 bf_status; + u16 bf_flags; /* tx descriptor flags */ + struct ath_buf_state bf_state; /* buffer state */ + dma_addr_t bf_dmacontext; +}; + +/* + * reset the rx buffer. + * any new fields added to the athbuf and require + * reset need to be added to this macro. + * currently bf_status is the only one requires that + * requires reset. + */ +#define ATH_RXBUF_RESET(_bf) ((_bf)->bf_status = 0) + +/* hw processing complete, desc processed by hal */ +#define ATH_BUFSTATUS_DONE 0x00000001 +/* hw processing complete, desc hold for hw */ +#define ATH_BUFSTATUS_STALE 0x00000002 +/* Rx-only: OS is done with this packet and it's ok to queued it to hw */ +#define ATH_BUFSTATUS_FREE 0x00000004 + +/* DMA state for tx/rx descriptors */ + +struct ath_descdma { + const char *dd_name; + struct ath_desc *dd_desc; /* descriptors */ + dma_addr_t dd_desc_paddr; /* physical addr of dd_desc */ + u32 dd_desc_len; /* size of dd_desc */ + struct ath_buf *dd_bufptr; /* associated buffers */ + dma_addr_t dd_dmacontext; +}; + +/* Abstraction of a received RX MPDU/MMPDU, or a RX fragment */ + +struct ath_rx_context { + struct ath_buf *ctx_rxbuf; /* associated ath_buf for rx */ +}; +#define ATH_RX_CONTEXT(skb) ((struct ath_rx_context *)skb->cb) + +int ath_descdma_setup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head, + const char *name, + int nbuf, + int ndesc); +int ath_desc_alloc(struct ath_softc *sc); +void ath_desc_free(struct ath_softc *sc); +void ath_descdma_cleanup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head); + +/******/ +/* RX */ +/******/ + +#define ATH_MAX_ANTENNA 3 +#define ATH_RXBUF 512 +#define ATH_RX_TIMEOUT 40 /* 40 milliseconds */ +#define WME_NUM_TID 16 +#define IEEE80211_BAR_CTL_TID_M 0xF000 /* tid mask */ +#define IEEE80211_BAR_CTL_TID_S 2 /* tid shift */ + +enum ATH_RX_TYPE { + ATH_RX_NON_CONSUMED = 0, + ATH_RX_CONSUMED +}; + +/* per frame rx status block */ +struct ath_recv_status { + u64 tsf; /* mac tsf */ + int8_t rssi; /* RSSI (noise floor ajusted) */ + int8_t rssictl[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */ + int8_t rssiextn[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */ + int8_t abs_rssi; /* absolute RSSI */ + u8 rateieee; /* data rate received (IEEE rate code) */ + u8 ratecode; /* phy rate code */ + int rateKbps; /* data rate received (Kbps) */ + int antenna; /* rx antenna */ + int flags; /* status of associated skb */ +#define ATH_RX_FCS_ERROR 0x01 +#define ATH_RX_MIC_ERROR 0x02 +#define ATH_RX_DECRYPT_ERROR 0x04 +#define ATH_RX_RSSI_VALID 0x08 +/* if any of ctl,extn chainrssis are valid */ +#define ATH_RX_CHAIN_RSSI_VALID 0x10 +/* if extn chain rssis are valid */ +#define ATH_RX_RSSI_EXTN_VALID 0x20 +/* set if 40Mhz, clear if 20Mhz */ +#define ATH_RX_40MHZ 0x40 +/* set if short GI, clear if full GI */ +#define ATH_RX_SHORT_GI 0x80 +}; + +struct ath_rxbuf { + struct sk_buff *rx_wbuf; /* buffer */ + unsigned long rx_time; /* system time when received */ + struct ath_recv_status rx_status; /* cached rx status */ +}; + +/* Per-TID aggregate receiver state for a node */ +struct ath_arx_tid { + struct ath_node *an; /* parent ath node */ + struct ath_rxbuf *rxbuf; /* re-ordering buffer */ + struct timer_list timer; + spinlock_t tidlock; /* lock to protect this TID structure */ + int baw_head; /* seq_next at head */ + int baw_tail; /* tail of block-ack window */ + int seq_reset; /* need to reset start sequence */ + int addba_exchangecomplete; + u16 seq_next; /* next expected sequence */ + u16 baw_size; /* block-ack window size */ +}; + +/* Per-node receiver aggregate state */ +struct ath_arx { + struct ath_arx_tid tid[WME_NUM_TID]; +}; + +int ath_startrecv(struct ath_softc *sc); +bool ath_stoprecv(struct ath_softc *sc); +void ath_flushrecv(struct ath_softc *sc); +u32 ath_calcrxfilter(struct ath_softc *sc); +void ath_rx_node_init(struct ath_softc *sc, struct ath_node *an); +void ath_rx_node_free(struct ath_softc *sc, struct ath_node *an); +void ath_rx_node_cleanup(struct ath_softc *sc, struct ath_node *an); +void ath_handle_rx_intr(struct ath_softc *sc); +int ath_rx_init(struct ath_softc *sc, int nbufs); +void ath_rx_cleanup(struct ath_softc *sc); +int ath_rx_tasklet(struct ath_softc *sc, int flush); +int ath_rx_input(struct ath_softc *sc, + struct ath_node *node, + int is_ampdu, + struct sk_buff *skb, + struct ath_recv_status *rx_status, + enum ATH_RX_TYPE *status); +int ath__rx_indicate(struct ath_softc *sc, + struct sk_buff *skb, + struct ath_recv_status *status, + u16 keyix); +int ath_rx_subframe(struct ath_node *an, struct sk_buff *skb, + struct ath_recv_status *status); + +/******/ +/* TX */ +/******/ + +#define ATH_FRAG_PER_MSDU 1 +#define ATH_TXBUF (512/ATH_FRAG_PER_MSDU) +/* max number of transmit attempts (tries) */ +#define ATH_TXMAXTRY 13 +/* max number of 11n transmit attempts (tries) */ +#define ATH_11N_TXMAXTRY 10 +/* max number of tries for management and control frames */ +#define ATH_MGT_TXMAXTRY 4 +#define WME_BA_BMP_SIZE 64 +#define WME_MAX_BA WME_BA_BMP_SIZE +#define ATH_TID_MAX_BUFS (2 * WME_MAX_BA) +#define TID_TO_WME_AC(_tid) \ + ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \ + (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \ + (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \ + WME_AC_VO) + + +/* Wireless Multimedia Extension Defines */ +#define WME_AC_BE 0 /* best effort */ +#define WME_AC_BK 1 /* background */ +#define WME_AC_VI 2 /* video */ +#define WME_AC_VO 3 /* voice */ +#define WME_NUM_AC 4 + +enum ATH_SM_PWRSAV{ + ATH_SM_ENABLE, + ATH_SM_PWRSAV_STATIC, + ATH_SM_PWRSAV_DYNAMIC, +}; + +/* + * Data transmit queue state. One of these exists for each + * hardware transmit queue. Packets sent to us from above + * are assigned to queues based on their priority. Not all + * devices support a complete set of hardware transmit queues. + * For those devices the array sc_ac2q will map multiple + * priorities to fewer hardware queues (typically all to one + * hardware queue). + */ +struct ath_txq { + u32 axq_qnum; /* hardware q number */ + u32 *axq_link; /* link ptr in last TX desc */ + struct list_head axq_q; /* transmit queue */ + spinlock_t axq_lock; /* lock on q and link */ + unsigned long axq_lockflags; /* intr state when must cli */ + u32 axq_depth; /* queue depth */ + u8 axq_aggr_depth; /* aggregates queued */ + u32 axq_totalqueued;/* total ever queued */ + u32 axq_intrcnt; /* count to determine + if descriptor should generate + int on this txq. */ + bool stopped; /* Is mac80211 queue + stopped ? */ + /* State for patching up CTS when bursting */ + struct ath_buf *axq_linkbuf; /* virtual addr of last buffer*/ + struct ath_desc *axq_lastdsWithCTS; /* first desc of the + last descriptor that contains CTS */ + struct ath_desc *axq_gatingds; /* final desc of the gating desc + * that determines whether lastdsWithCTS has + * been DMA'ed or not */ + struct list_head axq_acq; +}; + +/* per TID aggregate tx state for a destination */ +struct ath_atx_tid { + struct list_head list; /* round-robin tid entry */ + struct list_head buf_q; /* pending buffers */ + struct ath_node *an; /* parent node structure */ + struct ath_atx_ac *ac; /* parent access category */ + struct ath_buf *tx_buf[ATH_TID_MAX_BUFS];/* active tx frames */ + u16 seq_start; /* starting seq of BA window */ + u16 seq_next; /* next seq to be used */ + u16 baw_size; /* BA window size */ + int tidno; /* TID number */ + int baw_head; /* first un-acked tx buffer */ + int baw_tail; /* next unused tx buffer slot */ + int sched; /* TID is scheduled */ + int paused; /* TID is paused */ + int cleanup_inprogress; /* aggr of this TID is + being teared down */ + u32 addba_exchangecomplete:1; /* ADDBA state */ + int32_t addba_exchangeinprogress; + int addba_exchangeattempts; +}; + +/* per access-category aggregate tx state for a destination */ +struct ath_atx_ac { + int sched; /* dest-ac is scheduled */ + int qnum; /* H/W queue number associated + with this AC */ + struct list_head list; /* round-robin txq entry */ + struct list_head tid_q; /* queue of TIDs with buffers */ +}; + +/* per dest tx state */ +struct ath_atx { + struct ath_atx_tid tid[WME_NUM_TID]; + struct ath_atx_ac ac[WME_NUM_AC]; +}; + +/* per-frame tx control block */ +struct ath_tx_control { + struct ath_node *an; /* destination to sent to */ + int if_id; /* only valid for cab traffic */ + int qnum; /* h/w queue number */ + u32 ht:1; /* if it can be transmitted using HT */ + u32 ps:1; /* if one or more stations are in PS mode */ + u32 use_minrate:1; /* if this frame should transmitted using + minimum rate */ + enum ath9k_pkt_type atype; /* Atheros packet type */ + enum ath9k_key_type keytype; /* key type */ + u32 flags; /* HAL flags */ + u16 seqno; /* sequence number */ + u16 tidno; /* tid number */ + u16 txpower; /* transmit power */ + u16 frmlen; /* frame length */ + u32 keyix; /* key index */ + int min_rate; /* minimum rate */ + int mcast_rate; /* multicast rate */ + u16 nextfraglen; /* next fragment length */ + /* below is set only by ath_dev */ + struct ath_softc *dev; /* device handle */ + dma_addr_t dmacontext; +}; + +/* per frame tx status block */ +struct ath_xmit_status { + int retries; /* number of retries to successufully + transmit this frame */ + int flags; /* status of transmit */ +#define ATH_TX_ERROR 0x01 +#define ATH_TX_XRETRY 0x02 +#define ATH_TX_BAR 0x04 +}; + +struct ath_tx_stat { + int rssi; /* RSSI (noise floor ajusted) */ + int rssictl[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */ + int rssiextn[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */ + int rateieee; /* data rate xmitted (IEEE rate code) */ + int rateKbps; /* data rate xmitted (Kbps) */ + int ratecode; /* phy rate code */ + int flags; /* validity flags */ +/* if any of ctl,extn chain rssis are valid */ +#define ATH_TX_CHAIN_RSSI_VALID 0x01 +/* if extn chain rssis are valid */ +#define ATH_TX_RSSI_EXTN_VALID 0x02 + u32 airtime; /* time on air per final tx rate */ +}; + +struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype); +void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq); +int ath_tx_setup(struct ath_softc *sc, int haltype); +void ath_draintxq(struct ath_softc *sc, bool retry_tx); +void ath_tx_draintxq(struct ath_softc *sc, + struct ath_txq *txq, bool retry_tx); +void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an); +void ath_tx_node_cleanup(struct ath_softc *sc, + struct ath_node *an, bool bh_flag); +void ath_tx_node_free(struct ath_softc *sc, struct ath_node *an); +void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq); +int ath_tx_init(struct ath_softc *sc, int nbufs); +int ath_tx_cleanup(struct ath_softc *sc); +int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype); +int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_txq_info *q); +int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb); +void ath_tx_tasklet(struct ath_softc *sc); +u32 ath_txq_depth(struct ath_softc *sc, int qnum); +u32 ath_txq_aggr_depth(struct ath_softc *sc, int qnum); +void ath_notify_txq_status(struct ath_softc *sc, u16 queue_depth); +void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, + struct ath_xmit_status *tx_status, struct ath_node *an); + +/**********************/ +/* Node / Aggregation */ +/**********************/ + +/* indicates the node is clened up */ +#define ATH_NODE_CLEAN 0x1 +/* indicates the node is 80211 power save */ +#define ATH_NODE_PWRSAVE 0x2 + +#define ADDBA_TIMEOUT 200 /* 200 milliseconds */ +#define ADDBA_EXCHANGE_ATTEMPTS 10 +#define ATH_AGGR_DELIM_SZ 4 /* delimiter size */ +#define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */ +/* number of delimiters for encryption padding */ +#define ATH_AGGR_ENCRYPTDELIM 10 +/* minimum h/w qdepth to be sustained to maximize aggregation */ +#define ATH_AGGR_MIN_QDEPTH 2 +#define ATH_AMPDU_SUBFRAME_DEFAULT 32 +#define IEEE80211_SEQ_SEQ_SHIFT 4 +#define IEEE80211_SEQ_MAX 4096 +#define IEEE80211_MIN_AMPDU_BUF 0x8 + +/* return whether a bit at index _n in bitmap _bm is set + * _sz is the size of the bitmap */ +#define ATH_BA_ISSET(_bm, _n) (((_n) < (WME_BA_BMP_SIZE)) && \ + ((_bm)[(_n) >> 5] & (1 << ((_n) & 31)))) + +/* return block-ack bitmap index given sequence and starting sequence */ +#define ATH_BA_INDEX(_st, _seq) (((_seq) - (_st)) & (IEEE80211_SEQ_MAX - 1)) + +/* returns delimiter padding required given the packet length */ +#define ATH_AGGR_GET_NDELIM(_len) \ + (((((_len) + ATH_AGGR_DELIM_SZ) < ATH_AGGR_MINPLEN) ? \ + (ATH_AGGR_MINPLEN - (_len) - ATH_AGGR_DELIM_SZ) : 0) >> 2) + +#define BAW_WITHIN(_start, _bawsz, _seqno) \ + ((((_seqno) - (_start)) & 4095) < (_bawsz)) + +#define ATH_DS_BA_SEQ(_ds) ((_ds)->ds_us.tx.ts_seqnum) +#define ATH_DS_BA_BITMAP(_ds) (&(_ds)->ds_us.tx.ba_low) +#define ATH_DS_TX_BA(_ds) ((_ds)->ds_us.tx.ts_flags & ATH9K_TX_BA) +#define ATH_AN_2_TID(_an, _tidno) (&(_an)->an_aggr.tx.tid[(_tidno)]) + +enum ATH_AGGR_STATUS { + ATH_AGGR_DONE, + ATH_AGGR_BAW_CLOSED, + ATH_AGGR_LIMITED, + ATH_AGGR_SHORTPKT, + ATH_AGGR_8K_LIMITED, +}; + +enum ATH_AGGR_CHECK { + AGGR_NOT_REQUIRED, + AGGR_REQUIRED, + AGGR_CLEANUP_PROGRESS, + AGGR_EXCHANGE_PROGRESS, + AGGR_EXCHANGE_DONE +}; + +struct aggr_rifs_param { + int param_max_frames; + int param_max_len; + int param_rl; + int param_al; + struct ath_rc_series *param_rcs; +}; + +/* Per-node aggregation state */ +struct ath_node_aggr { + struct ath_atx tx; /* node transmit state */ + struct ath_arx rx; /* node receive state */ +}; + +/* driver-specific node state */ +struct ath_node { + struct list_head list; + struct ath_softc *an_sc; /* back pointer */ + atomic_t an_refcnt; + struct ath_chainmask_sel an_chainmask_sel; + struct ath_node_aggr an_aggr; /* A-MPDU aggregation state */ + u8 an_smmode; /* SM Power save mode */ + u8 an_flags; + u8 an_addr[ETH_ALEN]; +}; + +void ath_tx_resume_tid(struct ath_softc *sc, + struct ath_atx_tid *tid); +enum ATH_AGGR_CHECK ath_tx_aggr_check(struct ath_softc *sc, + struct ath_node *an, u8 tidno); +void ath_tx_aggr_teardown(struct ath_softc *sc, + struct ath_node *an, u8 tidno); +void ath_rx_aggr_teardown(struct ath_softc *sc, + struct ath_node *an, u8 tidno); +int ath_rx_aggr_start(struct ath_softc *sc, + const u8 *addr, + u16 tid, + u16 *ssn); +int ath_rx_aggr_stop(struct ath_softc *sc, + const u8 *addr, + u16 tid); +int ath_tx_aggr_start(struct ath_softc *sc, + const u8 *addr, + u16 tid, + u16 *ssn); +int ath_tx_aggr_stop(struct ath_softc *sc, + const u8 *addr, + u16 tid); +void ath_newassoc(struct ath_softc *sc, + struct ath_node *node, int isnew, int isuapsd); +struct ath_node *ath_node_attach(struct ath_softc *sc, + u8 addr[ETH_ALEN], int if_id); +void ath_node_detach(struct ath_softc *sc, struct ath_node *an, bool bh_flag); +struct ath_node *ath_node_get(struct ath_softc *sc, u8 addr[ETH_ALEN]); +void ath_node_put(struct ath_softc *sc, struct ath_node *an, bool bh_flag); +struct ath_node *ath_node_find(struct ath_softc *sc, u8 *addr); + +/*******************/ +/* Beacon Handling */ +/*******************/ + +/* + * Regardless of the number of beacons we stagger, (i.e. regardless of the + * number of BSSIDs) if a given beacon does not go out even after waiting this + * number of beacon intervals, the game's up. + */ +#define BSTUCK_THRESH (9 * ATH_BCBUF) +#define ATH_BCBUF 4 /* number of beacon buffers */ +#define ATH_DEFAULT_BINTVAL 100 /* default beacon interval in TU */ +#define ATH_DEFAULT_BMISS_LIMIT 10 +#define ATH_BEACON_AIFS_DEFAULT 0 /* Default aifs for ap beacon q */ +#define ATH_BEACON_CWMIN_DEFAULT 0 /* Default cwmin for ap beacon q */ +#define ATH_BEACON_CWMAX_DEFAULT 0 /* Default cwmax for ap beacon q */ +#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) + +/* beacon configuration */ +struct ath_beacon_config { + u16 beacon_interval; + u16 listen_interval; + u16 dtim_period; + u16 bmiss_timeout; + u8 dtim_count; + u8 tim_offset; + union { + u64 last_tsf; + u8 last_tstamp[8]; + } u; /* last received beacon/probe response timestamp of this BSS. */ +}; + +/* offsets in a beacon frame for + * quick acess of beacon content by low-level driver */ +struct ath_beacon_offset { + u8 *bo_tim; /* start of atim/dtim */ +}; + +void ath9k_beacon_tasklet(unsigned long data); +void ath_beacon_config(struct ath_softc *sc, int if_id); +int ath_beaconq_setup(struct ath_hal *ah); +int ath_beacon_alloc(struct ath_softc *sc, int if_id); +void ath_bstuck_process(struct ath_softc *sc); +void ath_beacon_tasklet(struct ath_softc *sc, int *needmark); +void ath_beacon_free(struct ath_softc *sc); +void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp); +void ath_beacon_sync(struct ath_softc *sc, int if_id); +void ath_update_beacon_info(struct ath_softc *sc, int avgbrssi); +void ath_get_beaconconfig(struct ath_softc *sc, + int if_id, + struct ath_beacon_config *conf); +int ath_update_beacon(struct ath_softc *sc, + int if_id, + struct ath_beacon_offset *bo, + struct sk_buff *skb, + int mcast); +/********/ +/* VAPs */ +/********/ + +#define ATH_IF_HW_OFF 0x0001 /* hardware state needs to turn off */ +#define ATH_IF_HW_ON 0x0002 /* hardware state needs to turn on */ +/* STA only: the associated AP is HT capable */ +#define ATH_IF_HT 0x0004 +/* AP/IBSS only: current BSS has privacy on */ +#define ATH_IF_PRIVACY 0x0008 +#define ATH_IF_BEACON_ENABLE 0x0010 /* AP/IBSS only: enable beacon */ +#define ATH_IF_BEACON_SYNC 0x0020 /* IBSS only: need to sync beacon */ + +/* + * Define the scheme that we select MAC address for multiple + * BSS on the same radio. The very first VAP will just use the MAC + * address from the EEPROM. For the next 3 VAPs, we set the + * U/L bit (bit 1) in MAC address, and use the next two bits as the + * index of the VAP. + */ + +#define ATH_SET_VAP_BSSID_MASK(bssid_mask) \ + ((bssid_mask)[0] &= ~(((ATH_BCBUF-1)<<2)|0x02)) + +/* VAP configuration (from protocol layer) */ +struct ath_vap_config { + u32 av_fixed_rateset; + u32 av_fixed_retryset; +}; + +/* driver-specific vap state */ +struct ath_vap { + struct ieee80211_vif *av_if_data; /* interface(vap) + instance from 802.11 protocal layer */ + enum ath9k_opmode av_opmode; /* VAP operational mode */ + struct ath_buf *av_bcbuf; /* beacon buffer */ + struct ath_beacon_offset av_boff; /* dynamic update state */ + struct ath_tx_control av_btxctl; /* tx control information + for beacon */ + int av_bslot; /* beacon slot index */ + struct ath_txq av_mcastq; /* multicast + transmit queue */ + struct ath_vap_config av_config; /* vap configuration + parameters from 802.11 protocol layer*/ + struct ath_rate_node *rc_node; +}; + +int ath_vap_attach(struct ath_softc *sc, + int if_id, + struct ieee80211_vif *if_data, + enum ath9k_opmode opmode); +int ath_vap_detach(struct ath_softc *sc, int if_id); +int ath_vap_config(struct ath_softc *sc, + int if_id, struct ath_vap_config *if_config); +int ath_vap_listen(struct ath_softc *sc, int if_id); + +/*********************/ +/* Antenna diversity */ +/*********************/ + +#define ATH_ANT_DIV_MAX_CFG 2 +#define ATH_ANT_DIV_MIN_IDLE_US 1000000 /* us */ +#define ATH_ANT_DIV_MIN_SCAN_US 50000 /* us */ + +enum ATH_ANT_DIV_STATE{ + ATH_ANT_DIV_IDLE, + ATH_ANT_DIV_SCAN, /* evaluating antenna */ +}; + +struct ath_antdiv { + struct ath_softc *antdiv_sc; + u8 antdiv_start; + enum ATH_ANT_DIV_STATE antdiv_state; + u8 antdiv_num_antcfg; + u8 antdiv_curcfg; + u8 antdiv_bestcfg; + int32_t antdivf_rssitrig; + int32_t antdiv_lastbrssi[ATH_ANT_DIV_MAX_CFG]; + u64 antdiv_lastbtsf[ATH_ANT_DIV_MAX_CFG]; + u64 antdiv_laststatetsf; + u8 antdiv_bssid[ETH_ALEN]; +}; + +void ath_slow_ant_div_init(struct ath_antdiv *antdiv, + struct ath_softc *sc, int32_t rssitrig); +void ath_slow_ant_div_start(struct ath_antdiv *antdiv, + u8 num_antcfg, + const u8 *bssid); +void ath_slow_ant_div_stop(struct ath_antdiv *antdiv); +void ath_slow_ant_div(struct ath_antdiv *antdiv, + struct ieee80211_hdr *wh, + struct ath_rx_status *rx_stats); +void ath_setdefantenna(void *sc, u32 antenna); + +/********************/ +/* Main driver core */ +/********************/ + +/* + * Default cache line size, in bytes. + * Used when PCI device not fully initialized by bootrom/BIOS +*/ +#define DEFAULT_CACHELINE 32 +#define ATH_DEFAULT_NOISE_FLOOR -95 +#define ATH_REGCLASSIDS_MAX 10 +#define ATH_CABQ_READY_TIME 80 /* % of beacon interval */ +#define ATH_PREAMBLE_SHORT (1<<0) +#define ATH_PROTECT_ENABLE (1<<1) +#define ATH_MAX_SW_RETRIES 10 +/* Num farmes difference in tx to flip default recv */ +#define ATH_ANTENNA_DIFF 2 +#define ATH_CHAN_MAX 255 +#define IEEE80211_WEP_NKID 4 /* number of key ids */ +#define IEEE80211_RATE_VAL 0x7f +/* + * The key cache is used for h/w cipher state and also for + * tracking station state such as the current tx antenna. + * We also setup a mapping table between key cache slot indices + * and station state to short-circuit node lookups on rx. + * Different parts have different size key caches. We handle + * up to ATH_KEYMAX entries (could dynamically allocate state). + */ +#define ATH_KEYMAX 128 /* max key cache size we handle */ + +#define RESET_RETRY_TXQ 0x00000001 +#define ATH_IF_ID_ANY 0xff + +#define ATH_TXPOWER_MAX 100 /* .5 dBm units */ + +#define RSSI_LPF_THRESHOLD -20 +#define ATH_RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */ +#define ATH_RATE_DUMMY_MARKER 0 +#define ATH_RSSI_LPF_LEN 10 +#define ATH_RSSI_DUMMY_MARKER 0x127 + +#define ATH_EP_MUL(x, mul) ((x) * (mul)) +#define ATH_EP_RND(x, mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) +#define ATH_RSSI_OUT(x) \ + (((x) != ATH_RSSI_DUMMY_MARKER) ? \ + (ATH_EP_RND((x), ATH_RSSI_EP_MULTIPLIER)) : ATH_RSSI_DUMMY_MARKER) +#define ATH_RSSI_IN(x) \ + (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER)) +#define ATH_LPF_RSSI(x, y, len) \ + ((x != ATH_RSSI_DUMMY_MARKER) ? \ + (((x) * ((len) - 1) + (y)) / (len)) : (y)) +#define ATH_RSSI_LPF(x, y) do { \ + if ((y) >= RSSI_LPF_THRESHOLD) \ + x = ATH_LPF_RSSI((x), \ + ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \ + } while (0) + + +enum PROT_MODE { + PROT_M_NONE = 0, + PROT_M_RTSCTS, + PROT_M_CTSONLY +}; + +enum RATE_TYPE { + NORMAL_RATE = 0, + HALF_RATE, + QUARTER_RATE +}; + +struct ath_ht_info { + enum ath9k_ht_macmode tx_chan_width; + u16 maxampdu; + u8 mpdudensity; + u8 ext_chan_offset; +}; + +struct ath_softc { + struct ieee80211_hw *hw; /* mac80211 instance */ + struct pci_dev *pdev; /* Bus handle */ + void __iomem *mem; /* address of the device */ + struct tasklet_struct intr_tq; /* General tasklet */ + struct tasklet_struct bcon_tasklet; /* Beacon tasklet */ + struct ath_config sc_config; /* per-instance load-time + parameters */ + int sc_debug; /* Debug masks */ + struct ath_hal *sc_ah; /* HAL Instance */ + struct ath_rate_softc *sc_rc; /* tx rate control support */ + u32 sc_intrstatus; /* HAL_STATUS */ + enum ath9k_opmode sc_opmode; /* current operating mode */ + + /* Properties, Config */ + u8 sc_invalid; /* being detached */ + u8 sc_beacons; /* beacons running */ + u8 sc_scanning; /* scanning active */ + u8 sc_txaggr; /* enable 11n tx aggregation */ + u8 sc_rxaggr; /* enable 11n rx aggregation */ + u8 sc_update_chainmask; /* change chain mask */ + u8 sc_full_reset; /* force full reset */ + enum wireless_mode sc_curmode; /* current phy mode */ + u16 sc_curtxpow; /* current tx power limit */ + u16 sc_curaid; /* current association id */ + u8 sc_curbssid[ETH_ALEN]; + u8 sc_myaddr[ETH_ALEN]; + enum PROT_MODE sc_protmode; /* protection mode */ + u8 sc_mcastantenna;/* Multicast antenna number */ + u8 sc_txantenna; /* data tx antenna + (fixed or auto) */ + u8 sc_nbcnvaps; /* # of vaps sending beacons */ + u16 sc_nvaps; /* # of active virtual ap's */ + struct ath_vap *sc_vaps[ATH_BCBUF]; /* interface id + to avp map */ + enum ath9k_int sc_imask; /* interrupt mask copy */ + u8 sc_bssidmask[ETH_ALEN]; + u8 sc_defant; /* current default antenna */ + u8 sc_rxotherant; /* rx's on non-default antenna*/ + u16 sc_cachelsz; /* cache line size */ + int sc_slotupdate; /* slot to next advance fsm */ + int sc_slottime; /* slot time */ + u8 sc_noreset; + int sc_bslot[ATH_BCBUF];/* beacon xmit slots */ + struct ath9k_node_stats sc_halstats; /* station-mode rssi stats */ + struct list_head node_list; + struct ath_ht_info sc_ht_info; + int16_t sc_noise_floor; /* signal noise floor in dBm */ + enum ath9k_ht_extprotspacing sc_ht_extprotspacing; + u8 sc_tx_chainmask; + u8 sc_rx_chainmask; + u8 sc_rxchaindetect_ref; + u8 sc_rxchaindetect_thresh5GHz; + u8 sc_rxchaindetect_thresh2GHz; + u8 sc_rxchaindetect_delta5GHz; + u8 sc_rxchaindetect_delta2GHz; + u32 sc_rtsaggrlimit; /* Chipset specific + aggr limit */ + u32 sc_flags; +#ifdef CONFIG_SLOW_ANT_DIV + /* Slow antenna diversity */ + struct ath_antdiv sc_antdiv; +#endif + enum { + OK, /* no change needed */ + UPDATE, /* update pending */ + COMMIT /* beacon sent, commit change */ + } sc_updateslot; /* slot time update fsm */ + + /* Crypto */ + u32 sc_keymax; /* size of key cache */ + DECLARE_BITMAP(sc_keymap, ATH_KEYMAX); /* key use bit map */ + u8 sc_splitmic; /* split TKIP MIC keys */ + int sc_keytype; /* type of the key being used */ + + /* RX */ + struct list_head sc_rxbuf; /* receive buffer */ + struct ath_descdma sc_rxdma; /* RX descriptors */ + int sc_rxbufsize; /* rx size based on mtu */ + u32 *sc_rxlink; /* link ptr in last RX desc */ + u32 sc_rxflush; /* rx flush in progress */ + u64 sc_lastrx; /* tsf of last rx'd frame */ + + /* TX */ + struct list_head sc_txbuf; /* transmit buffer */ + struct ath_txq sc_txq[ATH9K_NUM_TX_QUEUES]; + struct ath_descdma sc_txdma; /* TX descriptors */ + u32 sc_txqsetup; /* h/w queues setup */ + u32 sc_txintrperiod;/* tx interrupt batching */ + int sc_haltype2q[ATH9K_WME_AC_VO+1]; /* HAL WME + AC -> h/w qnum */ + u32 sc_ant_tx[8]; /* recent tx frames/antenna */ + + /* Beacon */ + struct ath9k_txq_info sc_beacon_qi; /* adhoc only: beacon + queue parameters */ + struct ath_descdma sc_bdma; /* beacon descriptors */ + struct ath_txq *sc_cabq; /* tx q for cab frames */ + struct list_head sc_bbuf; /* beacon buffers */ + u32 sc_bhalq; /* HAL q for outgoing beacons */ + u32 sc_bmisscount; /* missed beacon transmits */ + u32 ast_be_xmit; /* beacons transmitted */ + + /* Rate */ + struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX]; + const struct ath9k_rate_table *sc_rates[WIRELESS_MODE_MAX]; + const struct ath9k_rate_table *sc_currates; /* current rate table */ + u8 sc_rixmap[256]; /* IEEE to h/w + rate table ix */ + u8 sc_minrateix; /* min h/w rate index */ + u8 sc_protrix; /* protection rate index */ + struct { + u32 rateKbps; /* transfer rate in kbs */ + u8 ieeerate; /* IEEE rate */ + } sc_hwmap[256]; /* h/w rate ix mappings */ + + /* Channel, Band */ + struct ieee80211_channel channels[IEEE80211_NUM_BANDS][ATH_CHAN_MAX]; + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + struct ath9k_channel sc_curchan; /* current h/w channel */ + + /* Locks */ + spinlock_t sc_rxflushlock; /* lock of RX flush */ + spinlock_t sc_rxbuflock; /* rxbuf lock */ + spinlock_t sc_txbuflock; /* txbuf lock */ + spinlock_t sc_resetlock; + spinlock_t node_lock; +}; + +int ath_init(u16 devid, struct ath_softc *sc); +void ath_deinit(struct ath_softc *sc); +int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan); +int ath_suspend(struct ath_softc *sc); +irqreturn_t ath_isr(int irq, void *dev); +int ath_reset(struct ath_softc *sc); +void ath_scan_start(struct ath_softc *sc); +void ath_scan_end(struct ath_softc *sc); +int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan); +void ath_setup_rate(struct ath_softc *sc, + enum wireless_mode wMode, + enum RATE_TYPE type, + const struct ath9k_rate_table *rt); + +/*********************/ +/* Utility Functions */ +/*********************/ + +void ath_key_reset(struct ath_softc *sc, u16 keyix, int freeslot); +int ath_keyset(struct ath_softc *sc, + u16 keyix, + struct ath9k_keyval *hk, + const u8 mac[ETH_ALEN]); +int ath_get_hal_qnum(u16 queue, struct ath_softc *sc); +int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc); +void ath_setslottime(struct ath_softc *sc); +void ath_update_txpow(struct ath_softc *sc); +int ath_cabq_update(struct ath_softc *); +void ath_get_currentCountry(struct ath_softc *sc, + struct ath9k_country_entry *ctry); +u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp); +void ath_internal_reset(struct ath_softc *sc); +u32 ath_chan2flags(struct ieee80211_channel *chan, struct ath_softc *sc); +dma_addr_t ath_skb_map_single(struct ath_softc *sc, + struct sk_buff *skb, + int direction, + dma_addr_t *pa); +void ath_skb_unmap_single(struct ath_softc *sc, + struct sk_buff *skb, + int direction, + dma_addr_t *pa); +void ath_mcast_merge(struct ath_softc *sc, u32 mfilt[2]); +enum ath9k_ht_macmode ath_cwm_macmode(struct ath_softc *sc); + +#endif /* CORE_H */ diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c new file mode 100644 index 0000000..1f6f393 --- /dev/null +++ b/drivers/net/wireless/ath9k/hw.c @@ -0,0 +1,8563 @@ +/* + * 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. + */ + +#include +#include + +#include "core.h" +#include "hw.h" +#include "reg.h" +#include "phy.h" +#include "initvals.h" + +static void ath9k_hw_iqcal_collect(struct ath_hal *ah); +static void ath9k_hw_iqcalibrate(struct ath_hal *ah, u8 numChains); +static void ath9k_hw_adc_gaincal_collect(struct ath_hal *ah); +static void ath9k_hw_adc_gaincal_calibrate(struct ath_hal *ah, + u8 numChains); +static void ath9k_hw_adc_dccal_collect(struct ath_hal *ah); +static void ath9k_hw_adc_dccal_calibrate(struct ath_hal *ah, + u8 numChains); + +static const u8 CLOCK_RATE[] = { 40, 80, 22, 44, 88, 40 }; +static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93, -96 }; + +static const struct hal_percal_data iq_cal_multi_sample = { + IQ_MISMATCH_CAL, + MAX_CAL_SAMPLES, + PER_MIN_LOG_COUNT, + ath9k_hw_iqcal_collect, + ath9k_hw_iqcalibrate +}; +static const struct hal_percal_data iq_cal_single_sample = { + IQ_MISMATCH_CAL, + MIN_CAL_SAMPLES, + PER_MAX_LOG_COUNT, + ath9k_hw_iqcal_collect, + ath9k_hw_iqcalibrate +}; +static const struct hal_percal_data adc_gain_cal_multi_sample = { + ADC_GAIN_CAL, + MAX_CAL_SAMPLES, + PER_MIN_LOG_COUNT, + ath9k_hw_adc_gaincal_collect, + ath9k_hw_adc_gaincal_calibrate +}; +static const struct hal_percal_data adc_gain_cal_single_sample = { + ADC_GAIN_CAL, + MIN_CAL_SAMPLES, + PER_MAX_LOG_COUNT, + ath9k_hw_adc_gaincal_collect, + ath9k_hw_adc_gaincal_calibrate +}; +static const struct hal_percal_data adc_dc_cal_multi_sample = { + ADC_DC_CAL, + MAX_CAL_SAMPLES, + PER_MIN_LOG_COUNT, + ath9k_hw_adc_dccal_collect, + ath9k_hw_adc_dccal_calibrate +}; +static const struct hal_percal_data adc_dc_cal_single_sample = { + ADC_DC_CAL, + MIN_CAL_SAMPLES, + PER_MAX_LOG_COUNT, + ath9k_hw_adc_dccal_collect, + ath9k_hw_adc_dccal_calibrate +}; +static const struct hal_percal_data adc_init_dc_cal = { + ADC_DC_INIT_CAL, + MIN_CAL_SAMPLES, + INIT_LOG_COUNT, + ath9k_hw_adc_dccal_collect, + ath9k_hw_adc_dccal_calibrate +}; + +static const struct ath_hal ar5416hal = { + AR5416_MAGIC, + 0, + 0, + NULL, + NULL, + CTRY_DEFAULT, + 0, + 0, + 0, + 0, + 0, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, +}; + +static struct ath9k_rate_table ar5416_11a_table = { + 8, + {0}, + { + {true, PHY_OFDM, 6000, 0x0b, 0x00, (0x80 | 12), 0}, + {true, PHY_OFDM, 9000, 0x0f, 0x00, 18, 0}, + {true, PHY_OFDM, 12000, 0x0a, 0x00, (0x80 | 24), 2}, + {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 2}, + {true, PHY_OFDM, 24000, 0x09, 0x00, (0x80 | 48), 4}, + {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 4}, + {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 4}, + {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 4} + }, +}; + +static struct ath9k_rate_table ar5416_11b_table = { + 4, + {0}, + { + {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0}, + {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1}, + {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 1}, + {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 1} + }, +}; + +static struct ath9k_rate_table ar5416_11g_table = { + 12, + {0}, + { + {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0}, + {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1}, + {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 2}, + {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 3}, + + {false, PHY_OFDM, 6000, 0x0b, 0x00, 12, 4}, + {false, PHY_OFDM, 9000, 0x0f, 0x00, 18, 4}, + {true, PHY_OFDM, 12000, 0x0a, 0x00, 24, 6}, + {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 6}, + {true, PHY_OFDM, 24000, 0x09, 0x00, 48, 8}, + {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 8}, + {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 8}, + {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 8} + }, +}; + +static struct ath9k_rate_table ar5416_11ng_table = { + 28, + {0}, + { + {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0}, + {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1}, + {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 2}, + {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 3}, + + {false, PHY_OFDM, 6000, 0x0b, 0x00, 12, 4}, + {false, PHY_OFDM, 9000, 0x0f, 0x00, 18, 4}, + {true, PHY_OFDM, 12000, 0x0a, 0x00, 24, 6}, + {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 6}, + {true, PHY_OFDM, 24000, 0x09, 0x00, 48, 8}, + {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 8}, + {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 8}, + {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 8}, + {true, PHY_HT, 6500, 0x80, 0x00, 0, 4}, + {true, PHY_HT, 13000, 0x81, 0x00, 1, 6}, + {true, PHY_HT, 19500, 0x82, 0x00, 2, 6}, + {true, PHY_HT, 26000, 0x83, 0x00, 3, 8}, + {true, PHY_HT, 39000, 0x84, 0x00, 4, 8}, + {true, PHY_HT, 52000, 0x85, 0x00, 5, 8}, + {true, PHY_HT, 58500, 0x86, 0x00, 6, 8}, + {true, PHY_HT, 65000, 0x87, 0x00, 7, 8}, + {true, PHY_HT, 13000, 0x88, 0x00, 8, 4}, + {true, PHY_HT, 26000, 0x89, 0x00, 9, 6}, + {true, PHY_HT, 39000, 0x8a, 0x00, 10, 6}, + {true, PHY_HT, 52000, 0x8b, 0x00, 11, 8}, + {true, PHY_HT, 78000, 0x8c, 0x00, 12, 8}, + {true, PHY_HT, 104000, 0x8d, 0x00, 13, 8}, + {true, PHY_HT, 117000, 0x8e, 0x00, 14, 8}, + {true, PHY_HT, 130000, 0x8f, 0x00, 15, 8}, + }, +}; + +static struct ath9k_rate_table ar5416_11na_table = { + 24, + {0}, + { + {true, PHY_OFDM, 6000, 0x0b, 0x00, (0x80 | 12), 0}, + {true, PHY_OFDM, 9000, 0x0f, 0x00, 18, 0}, + {true, PHY_OFDM, 12000, 0x0a, 0x00, (0x80 | 24), 2}, + {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 2}, + {true, PHY_OFDM, 24000, 0x09, 0x00, (0x80 | 48), 4}, + {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 4}, + {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 4}, + {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 4}, + {true, PHY_HT, 6500, 0x80, 0x00, 0, 0}, + {true, PHY_HT, 13000, 0x81, 0x00, 1, 2}, + {true, PHY_HT, 19500, 0x82, 0x00, 2, 2}, + {true, PHY_HT, 26000, 0x83, 0x00, 3, 4}, + {true, PHY_HT, 39000, 0x84, 0x00, 4, 4}, + {true, PHY_HT, 52000, 0x85, 0x00, 5, 4}, + {true, PHY_HT, 58500, 0x86, 0x00, 6, 4}, + {true, PHY_HT, 65000, 0x87, 0x00, 7, 4}, + {true, PHY_HT, 13000, 0x88, 0x00, 8, 0}, + {true, PHY_HT, 26000, 0x89, 0x00, 9, 2}, + {true, PHY_HT, 39000, 0x8a, 0x00, 10, 2}, + {true, PHY_HT, 52000, 0x8b, 0x00, 11, 4}, + {true, PHY_HT, 78000, 0x8c, 0x00, 12, 4}, + {true, PHY_HT, 104000, 0x8d, 0x00, 13, 4}, + {true, PHY_HT, 117000, 0x8e, 0x00, 14, 4}, + {true, PHY_HT, 130000, 0x8f, 0x00, 15, 4}, + }, +}; + +static enum wireless_mode ath9k_hw_chan2wmode(struct ath_hal *ah, + const struct ath9k_channel *chan) +{ + if (IS_CHAN_CCK(chan)) + return WIRELESS_MODE_11b; + if (IS_CHAN_G(chan)) + return WIRELESS_MODE_11g; + return WIRELESS_MODE_11a; +} + +static bool ath9k_hw_wait(struct ath_hal *ah, + u32 reg, + u32 mask, + u32 val) +{ + int i; + + for (i = 0; i < (AH_TIMEOUT / AH_TIME_QUANTUM); i++) { + if ((REG_READ(ah, reg) & mask) == val) + return true; + + udelay(AH_TIME_QUANTUM); + } + DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO, + "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n", + __func__, reg, REG_READ(ah, reg), mask, val); + return false; +} + +static bool ath9k_hw_eeprom_read(struct ath_hal *ah, u32 off, + u16 *data) +{ + (void) REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); + + if (!ath9k_hw_wait(ah, + AR_EEPROM_STATUS_DATA, + AR_EEPROM_STATUS_DATA_BUSY | + AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0)) { + return false; + } + + *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA), + AR_EEPROM_STATUS_DATA_VAL); + + return true; +} + +static int ath9k_hw_flash_map(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + ahp->ah_cal_mem = ioremap(AR5416_EEPROM_START_ADDR, AR5416_EEPROM_MAX); + + if (!ahp->ah_cal_mem) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: cannot remap eeprom region \n", __func__); + return -EIO; + } + + return 0; +} + +static bool ath9k_hw_flash_read(struct ath_hal *ah, u32 off, + u16 *data) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + *data = ioread16(ahp->ah_cal_mem + off); + return true; +} + +static void ath9k_hw_read_revisions(struct ath_hal *ah) +{ + u32 val; + + val = REG_READ(ah, AR_SREV) & AR_SREV_ID; + + if (val == 0xFF) { + val = REG_READ(ah, AR_SREV); + + ah->ah_macVersion = + (val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S; + + ah->ah_macRev = MS(val, AR_SREV_REVISION2); + ah->ah_isPciExpress = + (val & AR_SREV_TYPE2_HOST_MODE) ? 0 : 1; + + } else { + if (!AR_SREV_9100(ah)) + ah->ah_macVersion = MS(val, AR_SREV_VERSION); + + ah->ah_macRev = val & AR_SREV_REVISION; + + if (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE) + ah->ah_isPciExpress = true; + } +} + +u32 ath9k_hw_reverse_bits(u32 val, u32 n) +{ + u32 retval; + int i; + + for (i = 0, retval = 0; i < n; i++) { + retval = (retval << 1) | (val & 1); + val >>= 1; + } + return retval; +} + +static void ath9k_hw_set_defaults(struct ath_hal *ah) +{ + int i; + + ah->ah_config.ath_hal_dma_beacon_response_time = 2; + ah->ah_config.ath_hal_sw_beacon_response_time = 10; + ah->ah_config.ath_hal_additional_swba_backoff = 0; + ah->ah_config.ath_hal_6mb_ack = 0x0; + ah->ah_config.ath_hal_cwmIgnoreExtCCA = 0; + ah->ah_config.ath_hal_pciePowerSaveEnable = 0; + ah->ah_config.ath_hal_pcieL1SKPEnable = 0; + ah->ah_config.ath_hal_pcieClockReq = 0; + ah->ah_config.ath_hal_pciePowerReset = 0x100; + ah->ah_config.ath_hal_pcieRestore = 0; + ah->ah_config.ath_hal_pcieWaen = 0; + ah->ah_config.ath_hal_analogShiftReg = 1; + ah->ah_config.ath_hal_htEnable = 1; + ah->ah_config.ath_hal_ofdmTrigLow = 200; + ah->ah_config.ath_hal_ofdmTrigHigh = 500; + ah->ah_config.ath_hal_cckTrigHigh = 200; + ah->ah_config.ath_hal_cckTrigLow = 100; + ah->ah_config.ath_hal_enableANI = 0; + ah->ah_config.ath_hal_noiseImmunityLvl = 4; + ah->ah_config.ath_hal_ofdmWeakSigDet = 1; + ah->ah_config.ath_hal_cckWeakSigThr = 0; + ah->ah_config.ath_hal_spurImmunityLvl = 2; + ah->ah_config.ath_hal_firStepLvl = 0; + ah->ah_config.ath_hal_rssiThrHigh = 40; + ah->ah_config.ath_hal_rssiThrLow = 7; + ah->ah_config.ath_hal_diversityControl = 0; + ah->ah_config.ath_hal_antennaSwitchSwap = 0; + + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + ah->ah_config.ath_hal_spurChans[i][0] = AR_NO_SPUR; + ah->ah_config.ath_hal_spurChans[i][1] = AR_NO_SPUR; + } + + ah->ah_config.ath_hal_intrMitigation = 0; +} + +static inline void ath9k_hw_override_ini(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + if (!AR_SREV_5416_V20_OR_LATER(ah) + || AR_SREV_9280_10_OR_LATER(ah)) + return; + + REG_WRITE(ah, 0x9800 + (651 << 2), 0x11); +} + +static inline void ath9k_hw_init_bb(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + u32 synthDelay; + + synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; + if (IS_CHAN_CCK(chan)) + synthDelay = (4 * synthDelay) / 22; + else + synthDelay /= 10; + + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + + udelay(synthDelay + BASE_ACTIVATE_DELAY); +} + +static inline void ath9k_hw_init_interrupt_masks(struct ath_hal *ah, + enum ath9k_opmode opmode) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + ahp->ah_maskReg = AR_IMR_TXERR | + AR_IMR_TXURN | + AR_IMR_RXERR | + AR_IMR_RXORN | + AR_IMR_BCNMISC; + + if (ahp->ah_intrMitigation) + ahp->ah_maskReg |= AR_IMR_RXINTM | AR_IMR_RXMINTR; + else + ahp->ah_maskReg |= AR_IMR_RXOK; + + ahp->ah_maskReg |= AR_IMR_TXOK; + + if (opmode == ATH9K_M_HOSTAP) + ahp->ah_maskReg |= AR_IMR_MIB; + + REG_WRITE(ah, AR_IMR, ahp->ah_maskReg); + REG_WRITE(ah, AR_IMR_S2, REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT); + + if (!AR_SREV_9100(ah)) { + REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF); + REG_WRITE(ah, AR_INTR_SYNC_ENABLE, AR_INTR_SYNC_DEFAULT); + REG_WRITE(ah, AR_INTR_SYNC_MASK, 0); + } +} + +static inline void ath9k_hw_init_qos(struct ath_hal *ah) +{ + REG_WRITE(ah, AR_MIC_QOS_CONTROL, 0x100aa); + REG_WRITE(ah, AR_MIC_QOS_SELECT, 0x3210); + + REG_WRITE(ah, AR_QOS_NO_ACK, + SM(2, AR_QOS_NO_ACK_TWO_BIT) | + SM(5, AR_QOS_NO_ACK_BIT_OFF) | + SM(0, AR_QOS_NO_ACK_BYTE_OFF)); + + REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL); + REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF); + REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF); + REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF); + REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); +} + +static void ath9k_hw_analog_shift_rmw(struct ath_hal *ah, + u32 reg, + u32 mask, + u32 shift, + u32 val) +{ + u32 regVal; + + regVal = REG_READ(ah, reg) & ~mask; + regVal |= (val << shift) & mask; + + REG_WRITE(ah, reg, regVal); + + if (ah->ah_config.ath_hal_analogShiftReg) + udelay(100); + + return; +} + +static u8 ath9k_hw_get_num_ant_config(struct ath_hal_5416 *ahp, + enum hal_freq_band freq_band) +{ + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + struct modal_eep_header *pModal = + &(eep->modalHeader[HAL_FREQ_BAND_2GHZ == freq_band]); + struct base_eep_header *pBase = &eep->baseEepHeader; + u8 num_ant_config; + + num_ant_config = 1; + + if (pBase->version >= 0x0E0D) + if (pModal->useAnt1) + num_ant_config += 1; + + return num_ant_config; +} + +static int +ath9k_hw_get_eeprom_antenna_cfg(struct ath_hal_5416 *ahp, + struct ath9k_channel *chan, + u8 index, + u16 *config) +{ + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + struct modal_eep_header *pModal = + &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); + struct base_eep_header *pBase = &eep->baseEepHeader; + + switch (index) { + case 0: + *config = pModal->antCtrlCommon & 0xFFFF; + return 0; + case 1: + if (pBase->version >= 0x0E0D) { + if (pModal->useAnt1) { + *config = + ((pModal->antCtrlCommon & 0xFFFF0000) >> 16); + return 0; + } + } + break; + default: + break; + } + + return -EINVAL; +} + +static inline bool ath9k_hw_nvram_read(struct ath_hal *ah, + u32 off, + u16 *data) +{ + if (ath9k_hw_use_flash(ah)) + return ath9k_hw_flash_read(ah, off, data); + else + return ath9k_hw_eeprom_read(ah, off, data); +} + +static inline bool ath9k_hw_fill_eeprom(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + u16 *eep_data; + int addr, ar5416_eep_start_loc = 0; + + if (!ath9k_hw_use_flash(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: Reading from EEPROM, not flash\n", __func__); + ar5416_eep_start_loc = 256; + } + if (AR_SREV_9100(ah)) + ar5416_eep_start_loc = 256; + + eep_data = (u16 *) eep; + for (addr = 0; + addr < sizeof(struct ar5416_eeprom) / sizeof(u16); + addr++) { + if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc, + eep_data)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: Unable to read eeprom region \n", + __func__); + return false; + } + eep_data++; + } + return true; +} + +/* XXX: Clean me up, make me more legible */ +static bool +ath9k_hw_eeprom_set_board_values(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_header *pModal; + int i, regChainOffset; + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + u8 txRxAttenLocal; + u16 ant_config; + + pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); + + txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44; + + ath9k_hw_get_eeprom_antenna_cfg(ahp, chan, 1, &ant_config); + REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config); + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + if (AR_SREV_9280(ah)) { + if (i >= 2) + break; + } + + if (AR_SREV_5416_V20_OR_LATER(ah) && + (ahp->ah_rxchainmask == 5 || ahp->ah_txchainmask == 5) + && (i != 0)) + regChainOffset = (i == 1) ? 0x2000 : 0x1000; + else + regChainOffset = i * 0x1000; + + REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, + pModal->antCtrlChain[i]); + + REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset, + (REG_READ(ah, + AR_PHY_TIMING_CTRL4(0) + + 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)); + + if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) { + if ((eep->baseEepHeader.version & + AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_3) { + txRxAttenLocal = pModal->txRxAttenCh[i]; + if (AR_SREV_9280_10_OR_LATER(ah)) { + REG_RMW_FIELD(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, + pModal-> + bswMargin[i]); + REG_RMW_FIELD(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN1_DB, + pModal-> + bswAtten[i]); + REG_RMW_FIELD(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, + pModal-> + xatten2Margin[i]); + REG_RMW_FIELD(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN2_DB, + pModal-> + xatten2Db[i]); + } else { + REG_WRITE(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + (REG_READ(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset) & + ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) + | SM(pModal-> + bswMargin[i], + AR_PHY_GAIN_2GHZ_BSW_MARGIN)); + REG_WRITE(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + (REG_READ(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset) & + ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) + | SM(pModal->bswAtten[i], + AR_PHY_GAIN_2GHZ_BSW_ATTEN)); + } + } + if (AR_SREV_9280_10_OR_LATER(ah)) { + REG_RMW_FIELD(ah, + AR_PHY_RXGAIN + + regChainOffset, + AR9280_PHY_RXGAIN_TXRX_ATTEN, + txRxAttenLocal); + REG_RMW_FIELD(ah, + AR_PHY_RXGAIN + + regChainOffset, + AR9280_PHY_RXGAIN_TXRX_MARGIN, + pModal->rxTxMarginCh[i]); + } else { + REG_WRITE(ah, + AR_PHY_RXGAIN + regChainOffset, + (REG_READ(ah, + AR_PHY_RXGAIN + + regChainOffset) & + ~AR_PHY_RXGAIN_TXRX_ATTEN) | + SM(txRxAttenLocal, + AR_PHY_RXGAIN_TXRX_ATTEN)); + REG_WRITE(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + (REG_READ(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset) & + ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | + SM(pModal->rxTxMarginCh[i], + AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); + } + } + } + + if (AR_SREV_9280_10_OR_LATER(ah)) { + if (IS_CHAN_2GHZ(chan)) { + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0, + AR_AN_RF2G1_CH0_OB, + AR_AN_RF2G1_CH0_OB_S, + pModal->ob); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0, + AR_AN_RF2G1_CH0_DB, + AR_AN_RF2G1_CH0_DB_S, + pModal->db); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1, + AR_AN_RF2G1_CH1_OB, + AR_AN_RF2G1_CH1_OB_S, + pModal->ob_ch1); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1, + AR_AN_RF2G1_CH1_DB, + AR_AN_RF2G1_CH1_DB_S, + pModal->db_ch1); + } else { + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0, + AR_AN_RF5G1_CH0_OB5, + AR_AN_RF5G1_CH0_OB5_S, + pModal->ob); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0, + AR_AN_RF5G1_CH0_DB5, + AR_AN_RF5G1_CH0_DB5_S, + pModal->db); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1, + AR_AN_RF5G1_CH1_OB5, + AR_AN_RF5G1_CH1_OB5_S, + pModal->ob_ch1); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1, + AR_AN_RF5G1_CH1_DB5, + AR_AN_RF5G1_CH1_DB5_S, + pModal->db_ch1); + } + ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2, + AR_AN_TOP2_XPABIAS_LVL, + AR_AN_TOP2_XPABIAS_LVL_S, + pModal->xpaBiasLvl); + ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2, + AR_AN_TOP2_LOCALBIAS, + AR_AN_TOP2_LOCALBIAS_S, + pModal->local_bias); + DPRINTF(ah->ah_sc, ATH_DBG_ANY, "ForceXPAon: %d\n", + pModal->force_xpaon); + REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG, + pModal->force_xpaon); + } + + REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, + pModal->switchSettling); + REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, + pModal->adcDesiredSize); + + if (!AR_SREV_9280_10_OR_LATER(ah)) + REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, + AR_PHY_DESIRED_SZ_PGA, + pModal->pgaDesiredSize); + + 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)); + + REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, + pModal->txEndToRxOn); + if (AR_SREV_9280_10_OR_LATER(ah)) { + REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, + pModal->thresh62); + REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, + AR_PHY_EXT_CCA0_THRESH62, + pModal->thresh62); + } else { + REG_RMW_FIELD(ah, AR_PHY_CCA, AR_PHY_CCA_THRESH62, + pModal->thresh62); + REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, + AR_PHY_EXT_CCA_THRESH62, + pModal->thresh62); + } + + if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, + AR_PHY_TX_END_DATA_START, + pModal->txFrameToDataStart); + REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_PA_ON, + pModal->txFrameToPaOn); + } + + if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_3) { + if (IS_CHAN_HT40(chan)) + REG_RMW_FIELD(ah, AR_PHY_SETTLING, + AR_PHY_SETTLING_SWITCH, + pModal->swSettleHt40); + } + + return true; +} + +static inline int ath9k_hw_check_eeprom(struct ath_hal *ah) +{ + u32 sum = 0, el; + u16 *eepdata; + int i; + struct ath_hal_5416 *ahp = AH5416(ah); + bool need_swap = false; + struct ar5416_eeprom *eep = + (struct ar5416_eeprom *) &ahp->ah_eeprom; + + if (!ath9k_hw_use_flash(ah)) { + u16 magic, magic2; + int addr; + + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, + &magic)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: Reading Magic # failed\n", __func__); + return false; + } + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "%s: Read Magic = 0x%04X\n", + __func__, magic); + + if (magic != AR5416_EEPROM_MAGIC) { + magic2 = swab16(magic); + + if (magic2 == AR5416_EEPROM_MAGIC) { + need_swap = true; + eepdata = (u16 *) (&ahp->ah_eeprom); + + for (addr = 0; + addr < + sizeof(struct ar5416_eeprom) / + sizeof(u16); addr++) { + u16 temp; + + temp = swab16(*eepdata); + *eepdata = temp; + eepdata++; + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "0x%04X ", *eepdata); + if (((addr + 1) % 6) == 0) + DPRINTF(ah->ah_sc, + ATH_DBG_EEPROM, + "\n"); + } + } else { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Invalid EEPROM Magic. " + "endianness missmatch.\n"); + return -EINVAL; + } + } + } + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", + need_swap ? "True" : "False"); + + if (need_swap) + el = swab16(ahp->ah_eeprom.baseEepHeader.length); + else + el = ahp->ah_eeprom.baseEepHeader.length; + + if (el > sizeof(struct ar5416_eeprom)) + el = sizeof(struct ar5416_eeprom) / sizeof(u16); + else + el = el / sizeof(u16); + + eepdata = (u16 *) (&ahp->ah_eeprom); + + for (i = 0; i < el; i++) + sum ^= *eepdata++; + + if (need_swap) { + u32 integer, j; + u16 word; + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "EEPROM Endianness is not native.. Changing \n"); + + word = swab16(eep->baseEepHeader.length); + eep->baseEepHeader.length = word; + + word = swab16(eep->baseEepHeader.checksum); + eep->baseEepHeader.checksum = word; + + word = swab16(eep->baseEepHeader.version); + eep->baseEepHeader.version = word; + + word = swab16(eep->baseEepHeader.regDmn[0]); + eep->baseEepHeader.regDmn[0] = word; + + word = swab16(eep->baseEepHeader.regDmn[1]); + eep->baseEepHeader.regDmn[1] = word; + + word = swab16(eep->baseEepHeader.rfSilent); + eep->baseEepHeader.rfSilent = word; + + word = swab16(eep->baseEepHeader.blueToothOptions); + eep->baseEepHeader.blueToothOptions = word; + + word = swab16(eep->baseEepHeader.deviceCap); + eep->baseEepHeader.deviceCap = word; + + for (j = 0; j < ARRAY_SIZE(eep->modalHeader); j++) { + struct modal_eep_header *pModal = + &eep->modalHeader[j]; + integer = swab32(pModal->antCtrlCommon); + pModal->antCtrlCommon = integer; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + integer = swab32(pModal->antCtrlChain[i]); + pModal->antCtrlChain[i] = integer; + } + + for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) { + word = swab16(pModal->spurChans[i].spurChan); + pModal->spurChans[i].spurChan = word; + } + } + } + + if (sum != 0xffff || ar5416_get_eep_ver(ahp) != AR5416_EEP_VER || + ar5416_get_eep_rev(ahp) < AR5416_EEP_NO_BACK_VER) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Bad EEPROM checksum 0x%x or revision 0x%04x\n", + sum, ar5416_get_eep_ver(ahp)); + return -EINVAL; + } + + return 0; +} + +static bool ath9k_hw_chip_test(struct ath_hal *ah) +{ + u32 regAddr[2] = { AR_STA_ID0, AR_PHY_BASE + (8 << 2) }; + u32 regHold[2]; + u32 patternData[4] = { 0x55555555, + 0xaaaaaaaa, + 0x66666666, + 0x99999999 }; + int i, j; + + for (i = 0; i < 2; i++) { + u32 addr = regAddr[i]; + u32 wrData, rdData; + + regHold[i] = REG_READ(ah, addr); + for (j = 0; j < 0x100; j++) { + wrData = (j << 16) | j; + REG_WRITE(ah, addr, wrData); + rdData = REG_READ(ah, addr); + if (rdData != wrData) { + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "%s: address test failed " + "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", + __func__, addr, wrData, rdData); + return false; + } + } + for (j = 0; j < 4; j++) { + wrData = patternData[j]; + REG_WRITE(ah, addr, wrData); + rdData = REG_READ(ah, addr); + if (wrData != rdData) { + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "%s: address test failed " + "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", + __func__, addr, wrData, rdData); + return false; + } + } + REG_WRITE(ah, regAddr[i], regHold[i]); + } + udelay(100); + return true; +} + +u32 ath9k_hw_getrxfilter(struct ath_hal *ah) +{ + u32 bits = REG_READ(ah, AR_RX_FILTER); + u32 phybits = REG_READ(ah, AR_PHY_ERR); + + if (phybits & AR_PHY_ERR_RADAR) + bits |= ATH9K_RX_FILTER_PHYRADAR; + if (phybits & (AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING)) + bits |= ATH9K_RX_FILTER_PHYERR; + return bits; +} + +void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits) +{ + u32 phybits; + + REG_WRITE(ah, AR_RX_FILTER, (bits & 0xffff) | AR_RX_COMPR_BAR); + phybits = 0; + if (bits & ATH9K_RX_FILTER_PHYRADAR) + phybits |= AR_PHY_ERR_RADAR; + if (bits & ATH9K_RX_FILTER_PHYERR) + phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING; + REG_WRITE(ah, AR_PHY_ERR, phybits); + + if (phybits) + REG_WRITE(ah, AR_RXCFG, + REG_READ(ah, AR_RXCFG) | AR_RXCFG_ZLFDMA); + else + REG_WRITE(ah, AR_RXCFG, + REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_ZLFDMA); +} + +bool ath9k_hw_setcapability(struct ath_hal *ah, + enum hal_capability_type type, + u32 capability, + u32 setting, + int *status) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 v; + + switch (type) { + case HAL_CAP_TKIP_MIC: + if (setting) + ahp->ah_staId1Defaults |= + AR_STA_ID1_CRPT_MIC_ENABLE; + else + ahp->ah_staId1Defaults &= + ~AR_STA_ID1_CRPT_MIC_ENABLE; + return true; + case HAL_CAP_DIVERSITY: + v = REG_READ(ah, AR_PHY_CCK_DETECT); + if (setting) + v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; + else + v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; + REG_WRITE(ah, AR_PHY_CCK_DETECT, v); + return true; + case HAL_CAP_MCAST_KEYSRCH: + if (setting) + ahp->ah_staId1Defaults |= AR_STA_ID1_MCAST_KSRCH; + else + ahp->ah_staId1Defaults &= ~AR_STA_ID1_MCAST_KSRCH; + return true; + case HAL_CAP_TSF_ADJUST: + if (setting) + ahp->ah_miscMode |= AR_PCU_TX_ADD_TSF; + else + ahp->ah_miscMode &= ~AR_PCU_TX_ADD_TSF; + return true; + default: + return false; + } +} + +void ath9k_hw_dmaRegDump(struct ath_hal *ah) +{ + u32 val[ATH9K_NUM_DMA_DEBUG_REGS]; + int qcuOffset = 0, dcuOffset = 0; + u32 *qcuBase = &val[0], *dcuBase = &val[4]; + int i; + + REG_WRITE(ah, AR_MACMISC, + ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | + (AR_MACMISC_MISC_OBS_BUS_1 << + AR_MACMISC_MISC_OBS_BUS_MSB_S))); + + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "Raw DMA Debug values:\n"); + for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) { + if (i % 4 == 0) + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n"); + + val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32))); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "%d: %08x ", i, val[i]); + } + + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n\n"); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); + + for (i = 0; i < ATH9K_NUM_QUEUES; + i++, qcuOffset += 4, dcuOffset += 5) { + if (i == 8) { + qcuOffset = 0; + qcuBase++; + } + + if (i == 6) { + dcuOffset = 0; + dcuBase++; + } + + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "%2d %2x %1x %2x %2x\n", + i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, + (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + + 3), + val[2] & (0x7 << (i * 3)) >> (i * 3), + (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); + } + + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n"); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "qcu_stitch state: %2x qcu_fetch state: %2x\n", + (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "qcu_complete state: %2x dcu_complete state: %2x\n", + (val[3] & 0x1c000000) >> 26, (val[6] & 0x3)); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "dcu_arb state: %2x dcu_fp state: %2x\n", + (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n", + (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "txfifo_valid_0: %1d txfifo_valid_1: %1d\n", + (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n", + (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); + + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "pcu observe 0x%x \n", + REG_READ(ah, AR_OBS_BUS_1)); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "AR_CR 0x%x \n", REG_READ(ah, AR_CR)); +} + +u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah, + u32 *rxc_pcnt, + u32 *rxf_pcnt, + u32 *txf_pcnt) +{ + static u32 cycles, rx_clear, rx_frame, tx_frame; + u32 good = 1; + + u32 rc = REG_READ(ah, AR_RCCNT); + u32 rf = REG_READ(ah, AR_RFCNT); + u32 tf = REG_READ(ah, AR_TFCNT); + u32 cc = REG_READ(ah, AR_CCCNT); + + if (cycles == 0 || cycles > cc) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: cycle counter wrap. ExtBusy = 0\n", + __func__); + good = 0; + } else { + u32 cc_d = cc - cycles; + u32 rc_d = rc - rx_clear; + u32 rf_d = rf - rx_frame; + u32 tf_d = tf - tx_frame; + + if (cc_d != 0) { + *rxc_pcnt = rc_d * 100 / cc_d; + *rxf_pcnt = rf_d * 100 / cc_d; + *txf_pcnt = tf_d * 100 / cc_d; + } else { + good = 0; + } + } + + cycles = cc; + rx_frame = rf; + rx_clear = rc; + tx_frame = tf; + + return good; +} + +void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode) +{ + u32 macmode; + + if (mode == ATH9K_HT_MACMODE_2040 && + !ah->ah_config.ath_hal_cwmIgnoreExtCCA) + macmode = AR_2040_JOINED_RX_CLEAR; + else + macmode = 0; + + REG_WRITE(ah, AR_2040_MODE, macmode); +} + +static void ath9k_hw_mark_phy_inactive(struct ath_hal *ah) +{ + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); +} + + +static struct ath_hal_5416 *ath9k_hw_newstate(u16 devid, + struct ath_softc *sc, + void __iomem *mem, + int *status) +{ + static const u8 defbssidmask[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + struct ath_hal_5416 *ahp; + struct ath_hal *ah; + + ahp = kzalloc(sizeof(struct ath_hal_5416), GFP_KERNEL); + if (ahp == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: cannot allocate memory for state block\n", + __func__); + *status = -ENOMEM; + return NULL; + } + + ah = &ahp->ah; + + memcpy(&ahp->ah, &ar5416hal, sizeof(struct ath_hal)); + + ah->ah_sc = sc; + ah->ah_sh = mem; + + ah->ah_devid = devid; + ah->ah_subvendorid = 0; + + ah->ah_flags = 0; + if ((devid == AR5416_AR9100_DEVID)) + ah->ah_macVersion = AR_SREV_VERSION_9100; + if (!AR_SREV_9100(ah)) + ah->ah_flags = AH_USE_EEPROM; + + ah->ah_powerLimit = MAX_RATE_POWER; + ah->ah_tpScale = ATH9K_TP_SCALE_MAX; + + ahp->ah_atimWindow = 0; + ahp->ah_diversityControl = ah->ah_config.ath_hal_diversityControl; + ahp->ah_antennaSwitchSwap = + ah->ah_config.ath_hal_antennaSwitchSwap; + + ahp->ah_staId1Defaults = AR_STA_ID1_CRPT_MIC_ENABLE; + ahp->ah_beaconInterval = 100; + ahp->ah_enable32kHzClock = DONT_USE_32KHZ; + ahp->ah_slottime = (u32) -1; + ahp->ah_acktimeout = (u32) -1; + ahp->ah_ctstimeout = (u32) -1; + ahp->ah_globaltxtimeout = (u32) -1; + memcpy(&ahp->ah_bssidmask, defbssidmask, ETH_ALEN); + + ahp->ah_gBeaconRate = 0; + + return ahp; +} + +static int ath9k_hw_eeprom_attach(struct ath_hal *ah) +{ + int status; + + if (ath9k_hw_use_flash(ah)) + ath9k_hw_flash_map(ah); + + if (!ath9k_hw_fill_eeprom(ah)) + return -EIO; + + status = ath9k_hw_check_eeprom(ah); + + return status; +} + +u32 ath9k_hw_get_eeprom(struct ath_hal_5416 *ahp, + enum eeprom_param param) +{ + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + struct modal_eep_header *pModal = eep->modalHeader; + struct base_eep_header *pBase = &eep->baseEepHeader; + + switch (param) { + case EEP_NFTHRESH_5: + return -pModal[0].noiseFloorThreshCh[0]; + case EEP_NFTHRESH_2: + return -pModal[1].noiseFloorThreshCh[0]; + case AR_EEPROM_MAC(0): + return pBase->macAddr[0] << 8 | pBase->macAddr[1]; + case AR_EEPROM_MAC(1): + return pBase->macAddr[2] << 8 | pBase->macAddr[3]; + case AR_EEPROM_MAC(2): + return pBase->macAddr[4] << 8 | pBase->macAddr[5]; + case EEP_REG_0: + return pBase->regDmn[0]; + case EEP_REG_1: + return pBase->regDmn[1]; + case EEP_OP_CAP: + return pBase->deviceCap; + case EEP_OP_MODE: + return pBase->opCapFlags; + case EEP_RF_SILENT: + return pBase->rfSilent; + case EEP_OB_5: + return pModal[0].ob; + case EEP_DB_5: + return pModal[0].db; + case EEP_OB_2: + return pModal[1].ob; + case EEP_DB_2: + return pModal[1].db; + case EEP_MINOR_REV: + return pBase->version & AR5416_EEP_VER_MINOR_MASK; + case EEP_TX_MASK: + return pBase->txMask; + case EEP_RX_MASK: + return pBase->rxMask; + default: + return 0; + } +} + +static inline int ath9k_hw_get_radiorev(struct ath_hal *ah) +{ + u32 val; + int i; + + REG_WRITE(ah, AR_PHY(0x36), 0x00007058); + for (i = 0; i < 8; i++) + REG_WRITE(ah, AR_PHY(0x20), 0x00010000); + val = (REG_READ(ah, AR_PHY(256)) >> 24) & 0xff; + val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4); + return ath9k_hw_reverse_bits(val, 8); +} + +static inline int ath9k_hw_init_macaddr(struct ath_hal *ah) +{ + u32 sum; + int i; + u16 eeval; + struct ath_hal_5416 *ahp = AH5416(ah); + DECLARE_MAC_BUF(mac); + + sum = 0; + for (i = 0; i < 3; i++) { + eeval = ath9k_hw_get_eeprom(ahp, AR_EEPROM_MAC(i)); + sum += eeval; + ahp->ah_macaddr[2 * i] = eeval >> 8; + ahp->ah_macaddr[2 * i + 1] = eeval & 0xff; + } + if (sum == 0 || sum == 0xffff * 3) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: mac address read failed: %s\n", __func__, + print_mac(mac, ahp->ah_macaddr)); + return -EADDRNOTAVAIL; + } + + return 0; +} + +static inline int16_t ath9k_hw_interpolate(u16 target, + u16 srcLeft, + u16 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 inline u16 ath9k_hw_fbin2freq(u8 fbin, + bool is2GHz) +{ + + if (fbin == AR5416_BCHAN_UNUSED) + return fbin; + + return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); +} + +static u16 ath9k_hw_eeprom_get_spur_chan(struct ath_hal *ah, + u16 i, + bool is2GHz) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416_eeprom *eep = + (struct ar5416_eeprom *) &ahp->ah_eeprom; + u16 spur_val = AR_NO_SPUR; + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "Getting spur idx %d is2Ghz. %d val %x\n", + i, is2GHz, ah->ah_config.ath_hal_spurChans[i][is2GHz]); + + switch (ah->ah_config.ath_hal_spurMode) { + case SPUR_DISABLE: + break; + case SPUR_ENABLE_IOCTL: + spur_val = ah->ah_config.ath_hal_spurChans[i][is2GHz]; + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "Getting spur val from new loc. %d\n", spur_val); + break; + case SPUR_ENABLE_EEPROM: + spur_val = eep->modalHeader[is2GHz].spurChans[i].spurChan; + break; + + } + return spur_val; +} + +static inline int ath9k_hw_rfattach(struct ath_hal *ah) +{ + bool rfStatus = false; + int ecode = 0; + + rfStatus = ath9k_hw_init_rf(ah, &ecode); + if (!rfStatus) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: RF setup failed, status %u\n", __func__, + ecode); + return ecode; + } + + return 0; +} + +static int ath9k_hw_rf_claim(struct ath_hal *ah) +{ + u32 val; + + REG_WRITE(ah, AR_PHY(0), 0x00000007); + + val = ath9k_hw_get_radiorev(ah); + switch (val & AR_RADIO_SREV_MAJOR) { + case 0: + val = AR_RAD5133_SREV_MAJOR; + break; + case AR_RAD5133_SREV_MAJOR: + case AR_RAD5122_SREV_MAJOR: + case AR_RAD2133_SREV_MAJOR: + case AR_RAD2122_SREV_MAJOR: + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: 5G Radio Chip Rev 0x%02X is not " + "supported by this driver\n", + __func__, ah->ah_analog5GhzRev); + return -EOPNOTSUPP; + } + + ah->ah_analog5GhzRev = val; + + return 0; +} + +static inline void ath9k_hw_init_pll(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + u32 pll; + + if (AR_SREV_9100(ah)) { + if (chan && IS_CHAN_5GHZ(chan)) + pll = 0x1450; + else + pll = 0x1458; + } else { + if (AR_SREV_9280_10_OR_LATER(ah)) { + pll = SM(0x5, AR_RTC_9160_PLL_REFDIV); + + if (chan && IS_CHAN_HALF_RATE(chan)) + pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL); + else if (chan && IS_CHAN_QUARTER_RATE(chan)) + pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL); + + if (chan && IS_CHAN_5GHZ(chan)) { + pll |= SM(0x28, AR_RTC_9160_PLL_DIV); + + + if (AR_SREV_9280_20(ah)) { + if (((chan->channel % 20) == 0) + || ((chan->channel % 10) == 0)) + pll = 0x2850; + else + pll = 0x142c; + } + } else { + pll |= SM(0x2c, AR_RTC_9160_PLL_DIV); + } + + } else if (AR_SREV_9160_10_OR_LATER(ah)) { + + pll = SM(0x5, AR_RTC_9160_PLL_REFDIV); + + if (chan && IS_CHAN_HALF_RATE(chan)) + pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL); + else if (chan && IS_CHAN_QUARTER_RATE(chan)) + pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL); + + if (chan && IS_CHAN_5GHZ(chan)) + pll |= SM(0x50, AR_RTC_9160_PLL_DIV); + else + pll |= SM(0x58, AR_RTC_9160_PLL_DIV); + } else { + pll = AR_RTC_PLL_REFDIV_5 | AR_RTC_PLL_DIV2; + + if (chan && IS_CHAN_HALF_RATE(chan)) + pll |= SM(0x1, AR_RTC_PLL_CLKSEL); + else if (chan && IS_CHAN_QUARTER_RATE(chan)) + pll |= SM(0x2, AR_RTC_PLL_CLKSEL); + + if (chan && IS_CHAN_5GHZ(chan)) + pll |= SM(0xa, AR_RTC_PLL_DIV); + else + pll |= SM(0xb, AR_RTC_PLL_DIV); + } + } + REG_WRITE(ah, (u16) (AR_RTC_PLL_CONTROL), pll); + + udelay(RTC_PLL_SETTLE_DELAY); + + REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK); +} + +static void ath9k_hw_set_regs(struct ath_hal *ah, struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode) +{ + u32 phymode; + struct ath_hal_5416 *ahp = AH5416(ah); + + phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40 + | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH; + + if (IS_CHAN_HT40(chan)) { + phymode |= AR_PHY_FC_DYN2040_EN; + + if ((chan->chanmode == CHANNEL_A_HT40PLUS) || + (chan->chanmode == CHANNEL_G_HT40PLUS)) + phymode |= AR_PHY_FC_DYN2040_PRI_CH; + + if (ahp->ah_extprotspacing == ATH9K_HT_EXTPROTSPACING_25) + phymode |= AR_PHY_FC_DYN2040_EXT_CH; + } + REG_WRITE(ah, AR_PHY_TURBO, phymode); + + ath9k_hw_set11nmac2040(ah, macmode); + + REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); + REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); +} + +static void ath9k_hw_set_operating_mode(struct ath_hal *ah, int opmode) +{ + u32 val; + + val = REG_READ(ah, AR_STA_ID1); + val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC); + switch (opmode) { + case ATH9K_M_HOSTAP: + REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP + | AR_STA_ID1_KSRCH_MODE); + REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); + break; + case ATH9K_M_IBSS: + REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC + | AR_STA_ID1_KSRCH_MODE); + REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); + break; + case ATH9K_M_STA: + case ATH9K_M_MONITOR: + REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE); + break; + } +} + +static inline void +ath9k_hw_set_rfmode(struct ath_hal *ah, struct ath9k_channel *chan) +{ + u32 rfMode = 0; + + if (chan == NULL) + return; + + rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) + ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; + + if (!AR_SREV_9280_10_OR_LATER(ah)) + rfMode |= (IS_CHAN_5GHZ(chan)) ? AR_PHY_MODE_RF5GHZ : + AR_PHY_MODE_RF2GHZ; + + if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) + rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); + + REG_WRITE(ah, AR_PHY_MODE, rfMode); +} + +static bool ath9k_hw_set_reset(struct ath_hal *ah, int type) +{ + u32 rst_flags; + u32 tmpReg; + + REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | + AR_RTC_FORCE_WAKE_ON_INT); + + if (AR_SREV_9100(ah)) { + rst_flags = AR_RTC_RC_MAC_WARM | AR_RTC_RC_MAC_COLD | + AR_RTC_RC_COLD_RESET | AR_RTC_RC_WARM_RESET; + } else { + tmpReg = REG_READ(ah, AR_INTR_SYNC_CAUSE); + if (tmpReg & + (AR_INTR_SYNC_LOCAL_TIMEOUT | + AR_INTR_SYNC_RADM_CPL_TIMEOUT)) { + REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0); + REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); + } else { + REG_WRITE(ah, AR_RC, AR_RC_AHB); + } + + rst_flags = AR_RTC_RC_MAC_WARM; + if (type == ATH9K_RESET_COLD) + rst_flags |= AR_RTC_RC_MAC_COLD; + } + + REG_WRITE(ah, (u16) (AR_RTC_RC), rst_flags); + udelay(50); + + REG_WRITE(ah, (u16) (AR_RTC_RC), 0); + if (!ath9k_hw_wait(ah, (u16) (AR_RTC_RC), AR_RTC_RC_M, 0)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: RTC stuck in MAC reset\n", + __func__); + return false; + } + + if (!AR_SREV_9100(ah)) + REG_WRITE(ah, AR_RC, 0); + + ath9k_hw_init_pll(ah, NULL); + + if (AR_SREV_9100(ah)) + udelay(50); + + return true; +} + +static inline bool ath9k_hw_set_reset_power_on(struct ath_hal *ah) +{ + REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | + AR_RTC_FORCE_WAKE_ON_INT); + + REG_WRITE(ah, (u16) (AR_RTC_RESET), 0); + REG_WRITE(ah, (u16) (AR_RTC_RESET), 1); + + if (!ath9k_hw_wait(ah, + AR_RTC_STATUS, + AR_RTC_STATUS_M, + AR_RTC_STATUS_ON)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: RTC not waking up\n", + __func__); + return false; + } + + ath9k_hw_read_revisions(ah); + + return ath9k_hw_set_reset(ah, ATH9K_RESET_WARM); +} + +static bool ath9k_hw_set_reset_reg(struct ath_hal *ah, + u32 type) +{ + REG_WRITE(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); + + switch (type) { + case ATH9K_RESET_POWER_ON: + return ath9k_hw_set_reset_power_on(ah); + break; + case ATH9K_RESET_WARM: + case ATH9K_RESET_COLD: + return ath9k_hw_set_reset(ah, type); + break; + default: + return false; + } +} + +static inline +struct ath9k_channel *ath9k_hw_check_chan(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + if (!(IS_CHAN_2GHZ(chan) ^ IS_CHAN_5GHZ(chan))) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel %u/0x%x; not marked as " + "2GHz or 5GHz\n", __func__, chan->channel, + chan->channelFlags); + return NULL; + } + + if (!IS_CHAN_OFDM(chan) && + !IS_CHAN_CCK(chan) && + !IS_CHAN_HT20(chan) && + !IS_CHAN_HT40(chan)) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel %u/0x%x; not marked as " + "OFDM or CCK or HT20 or HT40PLUS or HT40MINUS\n", + __func__, chan->channel, chan->channelFlags); + return NULL; + } + + return ath9k_regd_check_channel(ah, chan); +} + +static inline bool +ath9k_hw_get_lower_upper_index(u8 target, + u8 *pList, + u16 listSize, + u16 *indexL, + u16 *indexR) +{ + u16 i; + + if (target <= pList[0]) { + *indexL = *indexR = 0; + return true; + } + if (target >= pList[listSize - 1]) { + *indexL = *indexR = (u16) (listSize - 1); + return true; + } + + for (i = 0; i < listSize - 1; i++) { + if (pList[i] == target) { + *indexL = *indexR = i; + return true; + } + if (target < pList[i + 1]) { + *indexL = i; + *indexR = (u16) (i + 1); + return false; + } + } + return false; +} + +static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) +{ + int16_t nfval; + int16_t sort[ATH9K_NF_CAL_HIST_MAX]; + int i, j; + + for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++) + sort[i] = nfCalBuffer[i]; + + for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) { + for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) { + if (sort[j] > sort[j - 1]) { + nfval = sort[j]; + sort[j] = sort[j - 1]; + sort[j - 1] = nfval; + } + } + } + nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1]; + + return nfval; +} + +static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h, + int16_t *nfarray) +{ + int i; + + for (i = 0; i < NUM_NF_READINGS; i++) { + h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; + + if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX) + h[i].currIndex = 0; + + if (h[i].invalidNFcount > 0) { + if (nfarray[i] < AR_PHY_CCA_MIN_BAD_VALUE + || nfarray[i] > AR_PHY_CCA_MAX_HIGH_VALUE) { + h[i].invalidNFcount = ATH9K_NF_CAL_HIST_MAX; + } else { + h[i].invalidNFcount--; + h[i].privNF = nfarray[i]; + } + } else { + h[i].privNF = + ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); + } + } + return; +} + +static void ar5416GetNoiseFloor(struct ath_hal *ah, + int16_t nfarray[NUM_NF_READINGS]) +{ + int16_t nf; + + if (AR_SREV_9280_10_OR_LATER(ah)) + nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR); + else + nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR); + + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "NF calibrated [ctl] [chain 0] is %d\n", nf); + nfarray[0] = nf; + + if (AR_SREV_9280_10_OR_LATER(ah)) + nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), + AR9280_PHY_CH1_MINCCA_PWR); + else + nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), + AR_PHY_CH1_MINCCA_PWR); + + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL, + "NF calibrated [ctl] [chain 1] is %d\n", nf); + nfarray[1] = nf; + + if (!AR_SREV_9280(ah)) { + nf = MS(REG_READ(ah, AR_PHY_CH2_CCA), + AR_PHY_CH2_MINCCA_PWR); + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL, + "NF calibrated [ctl] [chain 2] is %d\n", nf); + nfarray[2] = nf; + } + + if (AR_SREV_9280_10_OR_LATER(ah)) + nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), + AR9280_PHY_EXT_MINCCA_PWR); + else + nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), + AR_PHY_EXT_MINCCA_PWR); + + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL, + "NF calibrated [ext] [chain 0] is %d\n", nf); + nfarray[3] = nf; + + if (AR_SREV_9280_10_OR_LATER(ah)) + nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), + AR9280_PHY_CH1_EXT_MINCCA_PWR); + else + nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), + AR_PHY_CH1_EXT_MINCCA_PWR); + + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "NF calibrated [ext] [chain 1] is %d\n", nf); + nfarray[4] = nf; + + if (!AR_SREV_9280(ah)) { + nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA), + AR_PHY_CH2_EXT_MINCCA_PWR); + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL, + "NF calibrated [ext] [chain 2] is %d\n", nf); + nfarray[5] = nf; + } +} + +static bool +getNoiseFloorThresh(struct ath_hal *ah, + const struct ath9k_channel *chan, + int16_t *nft) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + switch (chan->chanmode) { + case CHANNEL_A: + case CHANNEL_A_HT20: + case CHANNEL_A_HT40PLUS: + case CHANNEL_A_HT40MINUS: + *nft = (int16_t) ath9k_hw_get_eeprom(ahp, EEP_NFTHRESH_5); + break; + case CHANNEL_B: + case CHANNEL_G: + case CHANNEL_G_HT20: + case CHANNEL_G_HT40PLUS: + case CHANNEL_G_HT40MINUS: + *nft = (int16_t) ath9k_hw_get_eeprom(ahp, EEP_NFTHRESH_2); + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel flags 0x%x\n", __func__, + chan->channelFlags); + return false; + } + return true; +} + +static void ath9k_hw_start_nfcal(struct ath_hal *ah) +{ + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_ENABLE_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_NO_UPDATE_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); +} + +static void +ath9k_hw_loadnf(struct ath_hal *ah, struct ath9k_channel *chan) +{ + struct ath9k_nfcal_hist *h; + int i, j; + int32_t val; + const u32 ar5416_cca_regs[6] = { + AR_PHY_CCA, + AR_PHY_CH1_CCA, + AR_PHY_CH2_CCA, + AR_PHY_EXT_CCA, + AR_PHY_CH1_EXT_CCA, + AR_PHY_CH2_EXT_CCA + }; + u8 chainmask; + + if (AR_SREV_9280(ah)) + chainmask = 0x1B; + else + chainmask = 0x3F; + +#ifdef ATH_NF_PER_CHAN + h = chan->nfCalHist; +#else + h = ah->nfCalHist; +#endif + + for (i = 0; i < NUM_NF_READINGS; i++) { + if (chainmask & (1 << i)) { + val = REG_READ(ah, ar5416_cca_regs[i]); + val &= 0xFFFFFE00; + val |= (((u32) (h[i].privNF) << 1) & 0x1ff); + REG_WRITE(ah, ar5416_cca_regs[i], val); + } + } + + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_ENABLE_NF); + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_NO_UPDATE_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); + + for (j = 0; j < 1000; j++) { + if ((REG_READ(ah, AR_PHY_AGC_CONTROL) & + AR_PHY_AGC_CONTROL_NF) == 0) + break; + udelay(10); + } + + for (i = 0; i < NUM_NF_READINGS; i++) { + if (chainmask & (1 << i)) { + val = REG_READ(ah, ar5416_cca_regs[i]); + val &= 0xFFFFFE00; + val |= (((u32) (-50) << 1) & 0x1ff); + REG_WRITE(ah, ar5416_cca_regs[i], val); + } + } +} + +static int16_t ath9k_hw_getnf(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + int16_t nf, nfThresh; + int16_t nfarray[NUM_NF_READINGS] = { 0 }; + struct ath9k_nfcal_hist *h; + u8 chainmask; + + if (AR_SREV_9280(ah)) + chainmask = 0x1B; + else + chainmask = 0x3F; + + chan->channelFlags &= (~CHANNEL_CW_INT); + if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: NF did not complete in calibration window\n", + __func__); + nf = 0; + chan->rawNoiseFloor = nf; + return chan->rawNoiseFloor; + } else { + ar5416GetNoiseFloor(ah, nfarray); + nf = nfarray[0]; + if (getNoiseFloorThresh(ah, chan, &nfThresh) + && nf > nfThresh) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: noise floor failed detected; " + "detected %d, threshold %d\n", __func__, + nf, nfThresh); + chan->channelFlags |= CHANNEL_CW_INT; + } + } + +#ifdef ATH_NF_PER_CHAN + h = chan->nfCalHist; +#else + h = ah->nfCalHist; +#endif + + ath9k_hw_update_nfcal_hist_buffer(h, nfarray); + chan->rawNoiseFloor = h[0].privNF; + + return chan->rawNoiseFloor; +} + +static void ath9k_hw_update_mibstats(struct ath_hal *ah, + struct ath9k_mib_stats *stats) +{ + stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL); + stats->rts_bad += REG_READ(ah, AR_RTS_FAIL); + stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL); + stats->rts_good += REG_READ(ah, AR_RTS_OK); + stats->beacons += REG_READ(ah, AR_BEACON_CNT); +} + +static void ath9k_enable_mib_counters(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable mib counters\n"); + + ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats); + + REG_WRITE(ah, AR_FILT_OFDM, 0); + REG_WRITE(ah, AR_FILT_CCK, 0); + REG_WRITE(ah, AR_MIBC, + ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) + & 0x0f); + REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); + REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); +} + +static void ath9k_hw_disable_mib_counters(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disabling MIB counters\n"); + + REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC | AR_MIBC_CMC); + + ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats); + + REG_WRITE(ah, AR_FILT_OFDM, 0); + REG_WRITE(ah, AR_FILT_CCK, 0); +} + +static int ath9k_hw_get_ani_channel_idx(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) { + if (ahp->ah_ani[i].c.channel == chan->channel) + return i; + if (ahp->ah_ani[i].c.channel == 0) { + ahp->ah_ani[i].c.channel = chan->channel; + ahp->ah_ani[i].c.channelFlags = chan->channelFlags; + return i; + } + } + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "No more channel states left. Using channel 0\n"); + return 0; +} + +static void ath9k_hw_ani_attach(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + ahp->ah_hasHwPhyCounters = 1; + + memset(ahp->ah_ani, 0, sizeof(ahp->ah_ani)); + for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) { + ahp->ah_ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH; + ahp->ah_ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW; + ahp->ah_ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH; + ahp->ah_ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW; + ahp->ah_ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; + ahp->ah_ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; + ahp->ah_ani[i].ofdmWeakSigDetectOff = + !ATH9K_ANI_USE_OFDM_WEAK_SIG; + ahp->ah_ani[i].cckWeakSigThreshold = + ATH9K_ANI_CCK_WEAK_SIG_THR; + ahp->ah_ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; + ahp->ah_ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL; + if (ahp->ah_hasHwPhyCounters) { + ahp->ah_ani[i].ofdmPhyErrBase = + AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH; + ahp->ah_ani[i].cckPhyErrBase = + AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH; + } + } + if (ahp->ah_hasHwPhyCounters) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "Setting OfdmErrBase = 0x%08x\n", + ahp->ah_ani[0].ofdmPhyErrBase); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n", + ahp->ah_ani[0].cckPhyErrBase); + + REG_WRITE(ah, AR_PHY_ERR_1, ahp->ah_ani[0].ofdmPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_2, ahp->ah_ani[0].cckPhyErrBase); + ath9k_enable_mib_counters(ah); + } + ahp->ah_aniPeriod = ATH9K_ANI_PERIOD; + if (ah->ah_config.ath_hal_enableANI) + ahp->ah_procPhyErr |= HAL_PROCESS_ANI; +} + +static inline void ath9k_hw_ani_setup(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + const int totalSizeDesired[] = { -55, -55, -55, -55, -62 }; + const int coarseHigh[] = { -14, -14, -14, -14, -12 }; + const int coarseLow[] = { -64, -64, -64, -64, -70 }; + const int firpwr[] = { -78, -78, -78, -78, -80 }; + + for (i = 0; i < 5; i++) { + ahp->ah_totalSizeDesired[i] = totalSizeDesired[i]; + ahp->ah_coarseHigh[i] = coarseHigh[i]; + ahp->ah_coarseLow[i] = coarseLow[i]; + ahp->ah_firpwr[i] = firpwr[i]; + } +} + +static void ath9k_hw_ani_detach(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Detaching Ani\n"); + if (ahp->ah_hasHwPhyCounters) { + ath9k_hw_disable_mib_counters(ah); + REG_WRITE(ah, AR_PHY_ERR_1, 0); + REG_WRITE(ah, AR_PHY_ERR_2, 0); + } +} + + +static bool ath9k_hw_ani_control(struct ath_hal *ah, + enum ath9k_ani_cmd cmd, int param) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState = ahp->ah_curani; + + switch (cmd & ahp->ah_ani_function) { + case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{ + u32 level = param; + + if (level >= ARRAY_SIZE(ahp->ah_totalSizeDesired)) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: level out of range (%u > %u)\n", + __func__, level, + (unsigned) ARRAY_SIZE(ahp-> + ah_totalSizeDesired)); + return false; + } + + REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, + AR_PHY_DESIRED_SZ_TOT_DES, + ahp->ah_totalSizeDesired[level]); + REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, + AR_PHY_AGC_CTL1_COARSE_LOW, + ahp->ah_coarseLow[level]); + REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, + AR_PHY_AGC_CTL1_COARSE_HIGH, + ahp->ah_coarseHigh[level]); + REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, + AR_PHY_FIND_SIG_FIRPWR, + ahp->ah_firpwr[level]); + + if (level > aniState->noiseImmunityLevel) + ahp->ah_stats.ast_ani_niup++; + else if (level < aniState->noiseImmunityLevel) + ahp->ah_stats.ast_ani_nidown++; + aniState->noiseImmunityLevel = level; + break; + } + case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{ + const int m1ThreshLow[] = { 127, 50 }; + const int m2ThreshLow[] = { 127, 40 }; + const int m1Thresh[] = { 127, 0x4d }; + const int m2Thresh[] = { 127, 0x40 }; + const int m2CountThr[] = { 31, 16 }; + const int m2CountThrLow[] = { 63, 48 }; + u32 on = param ? 1 : 0; + + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M1_THRESH_LOW, + m1ThreshLow[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M2_THRESH_LOW, + m2ThreshLow[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M1_THRESH, + m1Thresh[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M2_THRESH, + m2Thresh[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M2COUNT_THR, + m2CountThr[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, + m2CountThrLow[on]); + + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M1_THRESH_LOW, + m1ThreshLow[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M2_THRESH_LOW, + m2ThreshLow[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M1_THRESH, + m1Thresh[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M2_THRESH, + m2Thresh[on]); + + if (on) + REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); + else + REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); + + if (!on != aniState->ofdmWeakSigDetectOff) { + if (on) + ahp->ah_stats.ast_ani_ofdmon++; + else + ahp->ah_stats.ast_ani_ofdmoff++; + aniState->ofdmWeakSigDetectOff = !on; + } + break; + } + case ATH9K_ANI_CCK_WEAK_SIGNAL_THR:{ + const int weakSigThrCck[] = { 8, 6 }; + u32 high = param ? 1 : 0; + + REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, + AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, + weakSigThrCck[high]); + if (high != aniState->cckWeakSigThreshold) { + if (high) + ahp->ah_stats.ast_ani_cckhigh++; + else + ahp->ah_stats.ast_ani_ccklow++; + aniState->cckWeakSigThreshold = high; + } + break; + } + case ATH9K_ANI_FIRSTEP_LEVEL:{ + const int firstep[] = { 0, 4, 8 }; + u32 level = param; + + if (level >= ARRAY_SIZE(firstep)) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: level out of range (%u > %u)\n", + __func__, level, + (unsigned) ARRAY_SIZE(firstep)); + return false; + } + REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, + AR_PHY_FIND_SIG_FIRSTEP, + firstep[level]); + if (level > aniState->firstepLevel) + ahp->ah_stats.ast_ani_stepup++; + else if (level < aniState->firstepLevel) + ahp->ah_stats.ast_ani_stepdown++; + aniState->firstepLevel = level; + break; + } + case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{ + const int cycpwrThr1[] = + { 2, 4, 6, 8, 10, 12, 14, 16 }; + u32 level = param; + + if (level >= ARRAY_SIZE(cycpwrThr1)) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: level out of range (%u > %u)\n", + __func__, level, + (unsigned) + ARRAY_SIZE(cycpwrThr1)); + return false; + } + REG_RMW_FIELD(ah, AR_PHY_TIMING5, + AR_PHY_TIMING5_CYCPWR_THR1, + cycpwrThr1[level]); + if (level > aniState->spurImmunityLevel) + ahp->ah_stats.ast_ani_spurup++; + else if (level < aniState->spurImmunityLevel) + ahp->ah_stats.ast_ani_spurdown++; + aniState->spurImmunityLevel = level; + break; + } + case ATH9K_ANI_PRESENT: + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: invalid cmd %u\n", __func__, cmd); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "%s: ANI parameters:\n", __func__); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "noiseImmunityLevel=%d, spurImmunityLevel=%d, " + "ofdmWeakSigDetectOff=%d\n", + aniState->noiseImmunityLevel, aniState->spurImmunityLevel, + !aniState->ofdmWeakSigDetectOff); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "cckWeakSigThreshold=%d, " + "firstepLevel=%d, listenTime=%d\n", + aniState->cckWeakSigThreshold, aniState->firstepLevel, + aniState->listenTime); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n", + aniState->cycleCount, aniState->ofdmPhyErrCount, + aniState->cckPhyErrCount); + return true; +} + +static void ath9k_ani_restart(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState; + + if (!DO_ANI(ah)) + return; + + aniState = ahp->ah_curani; + + aniState->listenTime = 0; + if (ahp->ah_hasHwPhyCounters) { + if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) { + aniState->ofdmPhyErrBase = 0; + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "OFDM Trigger is too high for hw counters\n"); + } else { + aniState->ofdmPhyErrBase = + AR_PHY_COUNTMAX - aniState->ofdmTrigHigh; + } + if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) { + aniState->cckPhyErrBase = 0; + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "CCK Trigger is too high for hw counters\n"); + } else { + aniState->cckPhyErrBase = + AR_PHY_COUNTMAX - aniState->cckTrigHigh; + } + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: Writing ofdmbase=%u cckbase=%u\n", + __func__, aniState->ofdmPhyErrBase, + aniState->cckPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); + REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); + + ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats); + } + aniState->ofdmPhyErrCount = 0; + aniState->cckPhyErrCount = 0; +} + +static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *chan = ah->ah_curchan; + struct ar5416AniState *aniState; + enum wireless_mode mode; + int32_t rssi; + + if (!DO_ANI(ah)) + return; + + aniState = ahp->ah_curani; + + if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, + aniState->noiseImmunityLevel + 1)) { + return; + } + } + + if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, + aniState->spurImmunityLevel + 1)) { + return; + } + } + + if (ah->ah_opmode == ATH9K_M_HOSTAP) { + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + } + return; + } + rssi = BEACON_RSSI(ahp); + if (rssi > aniState->rssiThrHigh) { + if (!aniState->ofdmWeakSigDetectOff) { + if (ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + false)) { + ath9k_hw_ani_control(ah, + ATH9K_ANI_SPUR_IMMUNITY_LEVEL, + 0); + return; + } + } + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + return; + } + } else if (rssi > aniState->rssiThrLow) { + if (aniState->ofdmWeakSigDetectOff) + ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + true); + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + return; + } else { + mode = ath9k_hw_chan2wmode(ah, chan); + if (mode == WIRELESS_MODE_11g || mode == WIRELESS_MODE_11b) { + if (!aniState->ofdmWeakSigDetectOff) + ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + false); + if (aniState->firstepLevel > 0) + ath9k_hw_ani_control(ah, + ATH9K_ANI_FIRSTEP_LEVEL, + 0); + return; + } + } +} + +static void ath9k_hw_ani_cck_err_trigger(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *chan = ah->ah_curchan; + struct ar5416AniState *aniState; + enum wireless_mode mode; + int32_t rssi; + + if (!DO_ANI(ah)) + return; + + aniState = ahp->ah_curani; + if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, + aniState->noiseImmunityLevel + 1)) { + return; + } + } + if (ah->ah_opmode == ATH9K_M_HOSTAP) { + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + } + return; + } + rssi = BEACON_RSSI(ahp); + if (rssi > aniState->rssiThrLow) { + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + } else { + mode = ath9k_hw_chan2wmode(ah, chan); + if (mode == WIRELESS_MODE_11g || mode == WIRELESS_MODE_11b) { + if (aniState->firstepLevel > 0) + ath9k_hw_ani_control(ah, + ATH9K_ANI_FIRSTEP_LEVEL, + 0); + } + } +} + +static void ath9k_ani_reset(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState; + struct ath9k_channel *chan = ah->ah_curchan; + int index; + + if (!DO_ANI(ah)) + return; + + index = ath9k_hw_get_ani_channel_idx(ah, chan); + aniState = &ahp->ah_ani[index]; + ahp->ah_curani = aniState; + + if (DO_ANI(ah) && ah->ah_opmode != ATH9K_M_STA + && ah->ah_opmode != ATH9K_M_IBSS) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: Reset ANI state opmode %u\n", __func__, + ah->ah_opmode); + ahp->ah_stats.ast_ani_reset++; + ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0); + ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0); + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0); + ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + !ATH9K_ANI_USE_OFDM_WEAK_SIG); + ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, + ATH9K_ANI_CCK_WEAK_SIG_THR); + ath9k_hw_setrxfilter(ah, + ath9k_hw_getrxfilter(ah) | + ATH9K_RX_FILTER_PHYERR); + if (ah->ah_opmode == ATH9K_M_HOSTAP) { + ahp->ah_curani->ofdmTrigHigh = + ah->ah_config.ath_hal_ofdmTrigHigh; + ahp->ah_curani->ofdmTrigLow = + ah->ah_config.ath_hal_ofdmTrigLow; + ahp->ah_curani->cckTrigHigh = + ah->ah_config.ath_hal_cckTrigHigh; + ahp->ah_curani->cckTrigLow = + ah->ah_config.ath_hal_cckTrigLow; + } + ath9k_ani_restart(ah); + return; + } + + if (aniState->noiseImmunityLevel != 0) + ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, + aniState->noiseImmunityLevel); + if (aniState->spurImmunityLevel != 0) + ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, + aniState->spurImmunityLevel); + if (aniState->ofdmWeakSigDetectOff) + ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + !aniState->ofdmWeakSigDetectOff); + if (aniState->cckWeakSigThreshold) + ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, + aniState->cckWeakSigThreshold); + if (aniState->firstepLevel != 0) + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel); + if (ahp->ah_hasHwPhyCounters) { + ath9k_hw_setrxfilter(ah, + ath9k_hw_getrxfilter(ah) & + ~ATH9K_RX_FILTER_PHYERR); + ath9k_ani_restart(ah); + REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); + REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); + + } else { + ath9k_ani_restart(ah); + ath9k_hw_setrxfilter(ah, + ath9k_hw_getrxfilter(ah) | + ATH9K_RX_FILTER_PHYERR); + } +} + +void ath9k_hw_procmibevent(struct ath_hal *ah, + const struct ath9k_node_stats *stats) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 phyCnt1, phyCnt2; + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Processing Mib Intr\n"); + + REG_WRITE(ah, AR_FILT_OFDM, 0); + REG_WRITE(ah, AR_FILT_CCK, 0); + if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING)) + REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); + + ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats); + ahp->ah_stats.ast_nodestats = *stats; + + if (!DO_ANI(ah)) + return; + + phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); + phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); + if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || + ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { + struct ar5416AniState *aniState = ahp->ah_curani; + u32 ofdmPhyErrCnt, cckPhyErrCnt; + + ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; + ahp->ah_stats.ast_ani_ofdmerrs += + ofdmPhyErrCnt - aniState->ofdmPhyErrCount; + aniState->ofdmPhyErrCount = ofdmPhyErrCnt; + + cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; + ahp->ah_stats.ast_ani_cckerrs += + cckPhyErrCnt - aniState->cckPhyErrCount; + aniState->cckPhyErrCount = cckPhyErrCnt; + + if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh) + ath9k_hw_ani_ofdm_err_trigger(ah); + if (aniState->cckPhyErrCount > aniState->cckTrigHigh) + ath9k_hw_ani_cck_err_trigger(ah); + + ath9k_ani_restart(ah); + } +} + +static void ath9k_hw_ani_lower_immunity(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState; + int32_t rssi; + + aniState = ahp->ah_curani; + + if (ah->ah_opmode == ATH9K_M_HOSTAP) { + if (aniState->firstepLevel > 0) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel - 1)) { + return; + } + } + } else { + rssi = BEACON_RSSI(ahp); + if (rssi > aniState->rssiThrHigh) { + /* XXX: Handle me */ + } else if (rssi > aniState->rssiThrLow) { + if (aniState->ofdmWeakSigDetectOff) { + if (ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + true) == + true) { + return; + } + } + if (aniState->firstepLevel > 0) { + if (ath9k_hw_ani_control + (ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel - 1) == + true) { + return; + } + } + } else { + if (aniState->firstepLevel > 0) { + if (ath9k_hw_ani_control + (ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel - 1) == + true) { + return; + } + } + } + } + + if (aniState->spurImmunityLevel > 0) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, + aniState->spurImmunityLevel - 1)) { + return; + } + } + + if (aniState->noiseImmunityLevel > 0) { + ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, + aniState->noiseImmunityLevel - 1); + return; + } +} + +static int32_t ath9k_hw_ani_get_listen_time(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState; + u32 txFrameCount, rxFrameCount, cycleCount; + int32_t listenTime; + + txFrameCount = REG_READ(ah, AR_TFCNT); + rxFrameCount = REG_READ(ah, AR_RFCNT); + cycleCount = REG_READ(ah, AR_CCCNT); + + aniState = ahp->ah_curani; + if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { + + listenTime = 0; + ahp->ah_stats.ast_ani_lzero++; + } else { + int32_t ccdelta = cycleCount - aniState->cycleCount; + int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; + int32_t tfdelta = txFrameCount - aniState->txFrameCount; + listenTime = (ccdelta - rfdelta - tfdelta) / 44000; + } + aniState->cycleCount = cycleCount; + aniState->txFrameCount = txFrameCount; + aniState->rxFrameCount = rxFrameCount; + + return listenTime; +} + +void ath9k_hw_ani_monitor(struct ath_hal *ah, + const struct ath9k_node_stats *stats, + struct ath9k_channel *chan) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState; + int32_t listenTime; + + aniState = ahp->ah_curani; + ahp->ah_stats.ast_nodestats = *stats; + + listenTime = ath9k_hw_ani_get_listen_time(ah); + if (listenTime < 0) { + ahp->ah_stats.ast_ani_lneg++; + ath9k_ani_restart(ah); + return; + } + + aniState->listenTime += listenTime; + + if (ahp->ah_hasHwPhyCounters) { + u32 phyCnt1, phyCnt2; + u32 ofdmPhyErrCnt, cckPhyErrCnt; + + ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats); + + phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); + phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); + + if (phyCnt1 < aniState->ofdmPhyErrBase || + phyCnt2 < aniState->cckPhyErrBase) { + if (phyCnt1 < aniState->ofdmPhyErrBase) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: phyCnt1 0x%x, resetting " + "counter value to 0x%x\n", + __func__, phyCnt1, + aniState->ofdmPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_1, + aniState->ofdmPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_MASK_1, + AR_PHY_ERR_OFDM_TIMING); + } + if (phyCnt2 < aniState->cckPhyErrBase) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: phyCnt2 0x%x, resetting " + "counter value to 0x%x\n", + __func__, phyCnt2, + aniState->cckPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_2, + aniState->cckPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_MASK_2, + AR_PHY_ERR_CCK_TIMING); + } + return; + } + + ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; + ahp->ah_stats.ast_ani_ofdmerrs += + ofdmPhyErrCnt - aniState->ofdmPhyErrCount; + aniState->ofdmPhyErrCount = ofdmPhyErrCnt; + + cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; + ahp->ah_stats.ast_ani_cckerrs += + cckPhyErrCnt - aniState->cckPhyErrCount; + aniState->cckPhyErrCount = cckPhyErrCnt; + } + + if (!DO_ANI(ah)) + return; + + if (aniState->listenTime > 5 * ahp->ah_aniPeriod) { + if (aniState->ofdmPhyErrCount <= aniState->listenTime * + aniState->ofdmTrigLow / 1000 && + aniState->cckPhyErrCount <= aniState->listenTime * + aniState->cckTrigLow / 1000) + ath9k_hw_ani_lower_immunity(ah); + ath9k_ani_restart(ah); + } else if (aniState->listenTime > ahp->ah_aniPeriod) { + if (aniState->ofdmPhyErrCount > aniState->listenTime * + aniState->ofdmTrigHigh / 1000) { + ath9k_hw_ani_ofdm_err_trigger(ah); + ath9k_ani_restart(ah); + } else if (aniState->cckPhyErrCount > + aniState->listenTime * aniState->cckTrigHigh / + 1000) { + ath9k_hw_ani_cck_err_trigger(ah); + ath9k_ani_restart(ah); + } + } +} + +#ifndef ATH_NF_PER_CHAN +static void ath9k_init_nfcal_hist_buffer(struct ath_hal *ah) +{ + int i, j; + + for (i = 0; i < NUM_NF_READINGS; i++) { + ah->nfCalHist[i].currIndex = 0; + ah->nfCalHist[i].privNF = AR_PHY_CCA_MAX_GOOD_VALUE; + ah->nfCalHist[i].invalidNFcount = + AR_PHY_CCA_FILTERWINDOW_LENGTH; + for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { + ah->nfCalHist[i].nfCalBuffer[j] = + AR_PHY_CCA_MAX_GOOD_VALUE; + } + } + return; +} +#endif + +static void ath9k_hw_gpio_cfg_output_mux(struct ath_hal *ah, + u32 gpio, u32 type) +{ + int addr; + u32 gpio_shift, tmp; + + if (gpio > 11) + addr = AR_GPIO_OUTPUT_MUX3; + else if (gpio > 5) + addr = AR_GPIO_OUTPUT_MUX2; + else + addr = AR_GPIO_OUTPUT_MUX1; + + gpio_shift = (gpio % 6) * 5; + + if (AR_SREV_9280_20_OR_LATER(ah) + || (addr != AR_GPIO_OUTPUT_MUX1)) { + REG_RMW(ah, addr, (type << gpio_shift), + (0x1f << gpio_shift)); + } else { + tmp = REG_READ(ah, addr); + tmp = ((tmp & 0x1F0) << 1) | (tmp & ~0x1F0); + tmp &= ~(0x1f << gpio_shift); + tmp |= (type << gpio_shift); + REG_WRITE(ah, addr, tmp); + } +} + +static bool ath9k_hw_cfg_output(struct ath_hal *ah, u32 gpio, + enum ath9k_gpio_output_mux_type + halSignalType) +{ + u32 ah_signal_type; + u32 gpio_shift; + + static u32 MuxSignalConversionTable[] = { + + AR_GPIO_OUTPUT_MUX_AS_OUTPUT, + + AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED, + + AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED, + + AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED, + + AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED, + }; + + if ((halSignalType >= 0) + && (halSignalType < ARRAY_SIZE(MuxSignalConversionTable))) + ah_signal_type = MuxSignalConversionTable[halSignalType]; + else + return false; + + ath9k_hw_gpio_cfg_output_mux(ah, gpio, ah_signal_type); + + gpio_shift = 2 * gpio; + + REG_RMW(ah, + AR_GPIO_OE_OUT, + (AR_GPIO_OE_OUT_DRV_ALL << gpio_shift), + (AR_GPIO_OE_OUT_DRV << gpio_shift)); + + return true; +} + +static bool ath9k_hw_set_gpio(struct ath_hal *ah, u32 gpio, + u32 val) +{ + REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio), + AR_GPIO_BIT(gpio)); + return true; +} + +static u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio) +{ + if (gpio >= ah->ah_caps.halNumGpioPins) + return 0xffffffff; + + if (AR_SREV_9280_10_OR_LATER(ah)) { + return (MS + (REG_READ(ah, AR_GPIO_IN_OUT), + AR928X_GPIO_IN_VAL) & AR_GPIO_BIT(gpio)) != 0; + } else { + return (MS(REG_READ(ah, AR_GPIO_IN_OUT), AR_GPIO_IN_VAL) & + AR_GPIO_BIT(gpio)) != 0; + } +} + +static inline int ath9k_hw_post_attach(struct ath_hal *ah) +{ + int ecode; + + if (!ath9k_hw_chip_test(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "%s: hardware self-test failed\n", __func__); + return -ENODEV; + } + + ecode = ath9k_hw_rf_claim(ah); + if (ecode != 0) + return ecode; + + ecode = ath9k_hw_eeprom_attach(ah); + if (ecode != 0) + return ecode; + ecode = ath9k_hw_rfattach(ah); + if (ecode != 0) + return ecode; + + if (!AR_SREV_9100(ah)) { + ath9k_hw_ani_setup(ah); + ath9k_hw_ani_attach(ah); + } + return 0; +} + +static u32 ath9k_hw_ini_fixup(struct ath_hal *ah, + struct ar5416_eeprom *pEepData, + u32 reg, u32 value) +{ + struct base_eep_header *pBase = &(pEepData->baseEepHeader); + + switch (ah->ah_devid) { + case AR9280_DEVID_PCI: + if (reg == 0x7894) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "ini VAL: %x EEPROM: %x\n", value, + (pBase->version & 0xff)); + + if ((pBase->version & 0xff) > 0x0a) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "PWDCLKIND: %d\n", + pBase->pwdclkind); + value &= ~AR_AN_TOP2_PWDCLKIND; + value |= AR_AN_TOP2_PWDCLKIND & (pBase-> + pwdclkind << AR_AN_TOP2_PWDCLKIND_S); + } else { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "PWDCLKIND Earlier Rev\n"); + } + + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "final ini VAL: %x\n", value); + } + break; + } + return value; +} + +static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_capabilities *pCap = &ah->ah_caps; + u16 capField = 0, eeval; + + eeval = ath9k_hw_get_eeprom(ahp, EEP_REG_0); + + ah->ah_currentRD = eeval; + + eeval = ath9k_hw_get_eeprom(ahp, EEP_REG_1); + ah->ah_currentRDExt = eeval; + + capField = ath9k_hw_get_eeprom(ahp, EEP_OP_CAP); + + if (ah->ah_opmode != ATH9K_M_HOSTAP && + ah->ah_subvendorid == AR_SUBVENDOR_ID_NEW_A) { + if (ah->ah_currentRD == 0x64 || ah->ah_currentRD == 0x65) + ah->ah_currentRD += 5; + else if (ah->ah_currentRD == 0x41) + ah->ah_currentRD = 0x43; + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: regdomain mapped to 0x%x\n", __func__, + ah->ah_currentRD); + } + + pCap->halWirelessModes = 0; + eeval = ath9k_hw_get_eeprom(ahp, EEP_OP_MODE); + + if (eeval & AR5416_OPFLAGS_11A) { + pCap->halWirelessModes |= ATH9K_MODE_SEL_11A | + ((!ah->ah_config.ath_hal_htEnable + || (eeval & AR5416_OPFLAGS_N_5G_HT20)) ? 0 + : (ATH9K_MODE_SEL_11NA_HT20 | + ((eeval & AR5416_OPFLAGS_N_5G_HT40) ? 0 + : (ATH9K_MODE_SEL_11NA_HT40PLUS | + ATH9K_MODE_SEL_11NA_HT40MINUS)))); + } + if (eeval & AR5416_OPFLAGS_11G) { + pCap->halWirelessModes |= + ATH9K_MODE_SEL_11B | ATH9K_MODE_SEL_11G | + ((!ah->ah_config.ath_hal_htEnable + || (eeval & AR5416_OPFLAGS_N_2G_HT20)) ? 0 + : (ATH9K_MODE_SEL_11NG_HT20 | + ((eeval & AR5416_OPFLAGS_N_2G_HT40) ? 0 + : (ATH9K_MODE_SEL_11NG_HT40PLUS | + ATH9K_MODE_SEL_11NG_HT40MINUS)))); + + } + pCap->halTxChainMask = ath9k_hw_get_eeprom(ahp, EEP_TX_MASK); + if ((ah->ah_isPciExpress) + || (eeval & AR5416_OPFLAGS_11A)) { + pCap->halRxChainMask = + ath9k_hw_get_eeprom(ahp, EEP_RX_MASK); + } else { + pCap->halRxChainMask = + (ath9k_hw_gpio_get(ah, 0)) ? 0x5 : 0x7; + } + + if (!(AR_SREV_9280(ah) && (ah->ah_macRev == 0))) + ahp->ah_miscMode |= AR_PCU_MIC_NEW_LOC_ENA; + + pCap->halLow2GhzChan = 2312; + pCap->halHigh2GhzChan = 2732; + + pCap->halLow5GhzChan = 4920; + pCap->halHigh5GhzChan = 6100; + + pCap->halCipherCkipSupport = false; + pCap->halCipherTkipSupport = true; + pCap->halCipherAesCcmSupport = true; + + pCap->halMicCkipSupport = false; + pCap->halMicTkipSupport = true; + pCap->halMicAesCcmSupport = true; + + pCap->halChanSpreadSupport = true; + + pCap->halHTSupport = + ah->ah_config.ath_hal_htEnable ? true : false; + pCap->halGTTSupport = true; + pCap->halVEOLSupport = true; + pCap->halBssIdMaskSupport = true; + pCap->halMcastKeySrchSupport = false; + + if (capField & AR_EEPROM_EEPCAP_MAXQCU) + pCap->halTotalQueues = + MS(capField, AR_EEPROM_EEPCAP_MAXQCU); + else + pCap->halTotalQueues = ATH9K_NUM_TX_QUEUES; + + if (capField & AR_EEPROM_EEPCAP_KC_ENTRIES) + pCap->halKeyCacheSize = + 1 << MS(capField, AR_EEPROM_EEPCAP_KC_ENTRIES); + else + pCap->halKeyCacheSize = AR_KEYTABLE_SIZE; + + pCap->halFastCCSupport = true; + pCap->halNumMRRetries = 4; + pCap->halTxTrigLevelMax = MAX_TX_FIFO_THRESHOLD; + + if (AR_SREV_9280_10_OR_LATER(ah)) + pCap->halNumGpioPins = AR928X_NUM_GPIO; + else + pCap->halNumGpioPins = AR_NUM_GPIO; + + if (AR_SREV_9280_10_OR_LATER(ah)) { + pCap->halWowSupport = true; + pCap->halWowMatchPatternExact = true; + } else { + pCap->halWowSupport = false; + pCap->halWowMatchPatternExact = false; + } + + if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah)) { + pCap->halCSTSupport = true; + pCap->halRtsAggrLimit = ATH_AMPDU_LIMIT_MAX; + } else { + pCap->halRtsAggrLimit = (8 * 1024); + } + + pCap->halEnhancedPmSupport = true; + + ah->ah_rfsilent = ath9k_hw_get_eeprom(ahp, EEP_RF_SILENT); + if (ah->ah_rfsilent & EEP_RFSILENT_ENABLED) { + ahp->ah_gpioSelect = + MS(ah->ah_rfsilent, EEP_RFSILENT_GPIO_SEL); + ahp->ah_polarity = + MS(ah->ah_rfsilent, EEP_RFSILENT_POLARITY); + + ath9k_hw_setcapability(ah, HAL_CAP_RFSILENT, 1, true, + NULL); + pCap->halRfSilentSupport = true; + } + + if ((ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) || + (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE) || + (ah->ah_macVersion == AR_SREV_VERSION_9160) || + (ah->ah_macVersion == AR_SREV_VERSION_9100) || + (ah->ah_macVersion == AR_SREV_VERSION_9280)) + pCap->halAutoSleepSupport = false; + else + pCap->halAutoSleepSupport = true; + + if (AR_SREV_9280(ah)) + pCap->hal4kbSplitTransSupport = false; + else + pCap->hal4kbSplitTransSupport = true; + + if (ah->ah_currentRDExt & (1 << REG_EXT_JAPAN_MIDBAND)) { + pCap->halRegCap = + AR_EEPROM_EEREGCAP_EN_KK_NEW_11A | + AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN | + AR_EEPROM_EEREGCAP_EN_KK_U2 | + AR_EEPROM_EEREGCAP_EN_KK_MIDBAND; + } else { + pCap->halRegCap = + AR_EEPROM_EEREGCAP_EN_KK_NEW_11A | + AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN; + } + + pCap->halRegCap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND; + + pCap->halNumAntCfg5GHz = + ath9k_hw_get_num_ant_config(ahp, HAL_FREQ_BAND_5GHZ); + pCap->halNumAntCfg2GHz = + ath9k_hw_get_num_ant_config(ahp, HAL_FREQ_BAND_2GHZ); + + return true; +} + +static void ar5416DisablePciePhy(struct ath_hal *ah) +{ + if (!AR_SREV_9100(ah)) + return; + + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + REG_WRITE(ah, AR_PCIE_SERDES, 0x28000029); + REG_WRITE(ah, AR_PCIE_SERDES, 0x57160824); + REG_WRITE(ah, AR_PCIE_SERDES, 0x25980579); + REG_WRITE(ah, AR_PCIE_SERDES, 0x00000000); + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + REG_WRITE(ah, AR_PCIE_SERDES, 0x000e1007); + + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); +} + +static void ath9k_set_power_sleep(struct ath_hal *ah, int setChip) +{ + REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); + if (setChip) { + REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN); + if (!AR_SREV_9100(ah)) + REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); + + REG_CLR_BIT(ah, (u16) (AR_RTC_RESET), + AR_RTC_RESET_EN); + } +} + +static void ath9k_set_power_network_sleep(struct ath_hal *ah, int setChip) +{ + REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); + if (setChip) { + struct hal_capabilities *pCap = &ah->ah_caps; + + if (!pCap->halAutoSleepSupport) { + REG_WRITE(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_ON_INT); + } else { + REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN); + } + } +} + +static bool ath9k_hw_set_power_awake(struct ath_hal *ah, + int setChip) +{ + u32 val; + int i; + + if (setChip) { + if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == + AR_RTC_STATUS_SHUTDOWN) { + if (ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON) + != true) { + return false; + } + } + if (AR_SREV_9100(ah)) + REG_SET_BIT(ah, AR_RTC_RESET, + AR_RTC_RESET_EN); + + REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN); + udelay(50); + + for (i = POWER_UP_TIME / 50; i > 0; i--) { + val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M; + if (val == AR_RTC_STATUS_ON) + break; + udelay(50); + REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN); + } + if (i == 0) { + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + "%s: Failed to wakeup in %uus\n", + __func__, POWER_UP_TIME / 20); + return false; + } + } + + REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); + return true; +} + +bool ath9k_hw_setpower(struct ath_hal *ah, + enum ath9k_power_mode mode) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + static const char *modes[] = { + "AWAKE", + "FULL-SLEEP", + "NETWORK SLEEP", + "UNDEFINED" + }; + int status = true, setChip = true; + + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, "%s: %s -> %s (%s)\n", __func__, + modes[ahp->ah_powerMode], modes[mode], + setChip ? "set chip " : ""); + + switch (mode) { + case ATH9K_PM_AWAKE: + status = ath9k_hw_set_power_awake(ah, setChip); + break; + case ATH9K_PM_FULL_SLEEP: + ath9k_set_power_sleep(ah, setChip); + ahp->ah_chipFullSleep = true; + break; + case ATH9K_PM_NETWORK_SLEEP: + ath9k_set_power_network_sleep(ah, setChip); + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + "%s: unknown power mode %u\n", __func__, mode); + return false; + } + ahp->ah_powerMode = mode; + return status; +} + +static struct ath_hal *ath9k_hw_do_attach(u16 devid, + struct ath_softc *sc, + void __iomem *mem, + int *status) +{ + struct ath_hal_5416 *ahp; + struct ath_hal *ah; + int ecode; +#ifndef CONFIG_SLOW_ANT_DIV + u32 i; + u32 j; +#endif + + ahp = ath9k_hw_newstate(devid, sc, mem, status); + if (ahp == NULL) + return NULL; + + ah = &ahp->ah; + + ath9k_hw_set_defaults(ah); + + if (ah->ah_config.ath_hal_intrMitigation != 0) + ahp->ah_intrMitigation = true; + + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: couldn't reset chip\n", + __func__); + ecode = -EIO; + goto bad; + } + + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: couldn't wakeup chip\n", + __func__); + ecode = -EIO; + goto bad; + } + + if (ah->ah_config.ath_hal_serializeRegMode == SER_REG_MODE_AUTO) { + if (ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) { + ah->ah_config.ath_hal_serializeRegMode = + SER_REG_MODE_ON; + } else { + ah->ah_config.ath_hal_serializeRegMode = + SER_REG_MODE_OFF; + } + } + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: ath_hal_serializeRegMode is %d\n", + __func__, ah->ah_config.ath_hal_serializeRegMode); + + if ((ah->ah_macVersion != AR_SREV_VERSION_5416_PCI) && + (ah->ah_macVersion != AR_SREV_VERSION_5416_PCIE) && + (ah->ah_macVersion != AR_SREV_VERSION_9160) && + (!AR_SREV_9100(ah)) && (!AR_SREV_9280(ah))) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: Mac Chip Rev 0x%02x.%x is not supported by " + "this driver\n", __func__, + ah->ah_macVersion, ah->ah_macRev); + ecode = -EOPNOTSUPP; + goto bad; + } + + if (AR_SREV_9100(ah)) { + ahp->ah_iqCalData.calData = &iq_cal_multi_sample; + ahp->ah_suppCals = IQ_MISMATCH_CAL; + ah->ah_isPciExpress = false; + } + ah->ah_phyRev = REG_READ(ah, AR_PHY_CHIP_ID); + + if (AR_SREV_9160_10_OR_LATER(ah)) { + if (AR_SREV_9280_10_OR_LATER(ah)) { + ahp->ah_iqCalData.calData = &iq_cal_single_sample; + ahp->ah_adcGainCalData.calData = + &adc_gain_cal_single_sample; + ahp->ah_adcDcCalData.calData = + &adc_dc_cal_single_sample; + ahp->ah_adcDcCalInitData.calData = + &adc_init_dc_cal; + } else { + ahp->ah_iqCalData.calData = &iq_cal_multi_sample; + ahp->ah_adcGainCalData.calData = + &adc_gain_cal_multi_sample; + ahp->ah_adcDcCalData.calData = + &adc_dc_cal_multi_sample; + ahp->ah_adcDcCalInitData.calData = + &adc_init_dc_cal; + } + ahp->ah_suppCals = + ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; + } + + if (AR_SREV_9160(ah)) { + ah->ah_config.ath_hal_enableANI = 1; + ahp->ah_ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL | + ATH9K_ANI_FIRSTEP_LEVEL); + } else { + ahp->ah_ani_function = ATH9K_ANI_ALL; + if (AR_SREV_9280_10_OR_LATER(ah)) { + ahp->ah_ani_function &= + ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL; + } + } + + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: This Mac Chip Rev 0x%02x.%x is \n", __func__, + ah->ah_macVersion, ah->ah_macRev); + + if (AR_SREV_9280_20_OR_LATER(ah)) { + INIT_INI_ARRAY(&ahp->ah_iniModes, ar9280Modes_9280_2, + ARRAY_SIZE(ar9280Modes_9280_2), 6); + INIT_INI_ARRAY(&ahp->ah_iniCommon, ar9280Common_9280_2, + ARRAY_SIZE(ar9280Common_9280_2), 2); + + if (ah->ah_config.ath_hal_pcieClockReq) { + INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes, + ar9280PciePhy_clkreq_off_L1_9280, + ARRAY_SIZE + (ar9280PciePhy_clkreq_off_L1_9280), + 2); + } else { + INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes, + ar9280PciePhy_clkreq_always_on_L1_9280, + ARRAY_SIZE + (ar9280PciePhy_clkreq_always_on_L1_9280), + 2); + } + INIT_INI_ARRAY(&ahp->ah_iniModesAdditional, + ar9280Modes_fast_clock_9280_2, + ARRAY_SIZE(ar9280Modes_fast_clock_9280_2), + 3); + } else if (AR_SREV_9280_10_OR_LATER(ah)) { + INIT_INI_ARRAY(&ahp->ah_iniModes, ar9280Modes_9280, + ARRAY_SIZE(ar9280Modes_9280), 6); + INIT_INI_ARRAY(&ahp->ah_iniCommon, ar9280Common_9280, + ARRAY_SIZE(ar9280Common_9280), 2); + } else if (AR_SREV_9160_10_OR_LATER(ah)) { + INIT_INI_ARRAY(&ahp->ah_iniModes, ar5416Modes_9160, + ARRAY_SIZE(ar5416Modes_9160), 6); + INIT_INI_ARRAY(&ahp->ah_iniCommon, ar5416Common_9160, + ARRAY_SIZE(ar5416Common_9160), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank0, ar5416Bank0_9160, + ARRAY_SIZE(ar5416Bank0_9160), 2); + INIT_INI_ARRAY(&ahp->ah_iniBB_RfGain, ar5416BB_RfGain_9160, + ARRAY_SIZE(ar5416BB_RfGain_9160), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank1, ar5416Bank1_9160, + ARRAY_SIZE(ar5416Bank1_9160), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank2, ar5416Bank2_9160, + ARRAY_SIZE(ar5416Bank2_9160), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank3, ar5416Bank3_9160, + ARRAY_SIZE(ar5416Bank3_9160), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6, ar5416Bank6_9160, + ARRAY_SIZE(ar5416Bank6_9160), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6TPC, ar5416Bank6TPC_9160, + ARRAY_SIZE(ar5416Bank6TPC_9160), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank7, ar5416Bank7_9160, + ARRAY_SIZE(ar5416Bank7_9160), 2); + if (AR_SREV_9160_11(ah)) { + INIT_INI_ARRAY(&ahp->ah_iniAddac, + ar5416Addac_91601_1, + ARRAY_SIZE(ar5416Addac_91601_1), 2); + } else { + INIT_INI_ARRAY(&ahp->ah_iniAddac, ar5416Addac_9160, + ARRAY_SIZE(ar5416Addac_9160), 2); + } + } else if (AR_SREV_9100_OR_LATER(ah)) { + INIT_INI_ARRAY(&ahp->ah_iniModes, ar5416Modes_9100, + ARRAY_SIZE(ar5416Modes_9100), 6); + INIT_INI_ARRAY(&ahp->ah_iniCommon, ar5416Common_9100, + ARRAY_SIZE(ar5416Common_9100), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank0, ar5416Bank0_9100, + ARRAY_SIZE(ar5416Bank0_9100), 2); + INIT_INI_ARRAY(&ahp->ah_iniBB_RfGain, ar5416BB_RfGain_9100, + ARRAY_SIZE(ar5416BB_RfGain_9100), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank1, ar5416Bank1_9100, + ARRAY_SIZE(ar5416Bank1_9100), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank2, ar5416Bank2_9100, + ARRAY_SIZE(ar5416Bank2_9100), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank3, ar5416Bank3_9100, + ARRAY_SIZE(ar5416Bank3_9100), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6, ar5416Bank6_9100, + ARRAY_SIZE(ar5416Bank6_9100), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6TPC, ar5416Bank6TPC_9100, + ARRAY_SIZE(ar5416Bank6TPC_9100), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank7, ar5416Bank7_9100, + ARRAY_SIZE(ar5416Bank7_9100), 2); + INIT_INI_ARRAY(&ahp->ah_iniAddac, ar5416Addac_9100, + ARRAY_SIZE(ar5416Addac_9100), 2); + } else { + INIT_INI_ARRAY(&ahp->ah_iniModes, ar5416Modes, + ARRAY_SIZE(ar5416Modes), 6); + INIT_INI_ARRAY(&ahp->ah_iniCommon, ar5416Common, + ARRAY_SIZE(ar5416Common), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank0, ar5416Bank0, + ARRAY_SIZE(ar5416Bank0), 2); + INIT_INI_ARRAY(&ahp->ah_iniBB_RfGain, ar5416BB_RfGain, + ARRAY_SIZE(ar5416BB_RfGain), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank1, ar5416Bank1, + ARRAY_SIZE(ar5416Bank1), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank2, ar5416Bank2, + ARRAY_SIZE(ar5416Bank2), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank3, ar5416Bank3, + ARRAY_SIZE(ar5416Bank3), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6, ar5416Bank6, + ARRAY_SIZE(ar5416Bank6), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6TPC, ar5416Bank6TPC, + ARRAY_SIZE(ar5416Bank6TPC), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank7, ar5416Bank7, + ARRAY_SIZE(ar5416Bank7), 2); + INIT_INI_ARRAY(&ahp->ah_iniAddac, ar5416Addac, + ARRAY_SIZE(ar5416Addac), 2); + } + + if (ah->ah_isPciExpress) + ath9k_hw_configpcipowersave(ah, 0); + else + ar5416DisablePciePhy(ah); + + ecode = ath9k_hw_post_attach(ah); + if (ecode != 0) + goto bad; + +#ifndef CONFIG_SLOW_ANT_DIV + if (ah->ah_devid == AR9280_DEVID_PCI) { + for (i = 0; i < ahp->ah_iniModes.ia_rows; i++) { + u32 reg = INI_RA(&ahp->ah_iniModes, i, 0); + + for (j = 1; j < ahp->ah_iniModes.ia_columns; j++) { + u32 val = INI_RA(&ahp->ah_iniModes, i, j); + + INI_RA(&ahp->ah_iniModes, i, j) = + ath9k_hw_ini_fixup(ah, &ahp->ah_eeprom, + reg, val); + } + } + } +#endif + + if (!ath9k_hw_fill_cap_info(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s:failed ath9k_hw_fill_cap_info\n", __func__); + ecode = -EINVAL; + goto bad; + } + + ecode = ath9k_hw_init_macaddr(ah); + if (ecode != 0) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: failed initializing mac address\n", + __func__); + goto bad; + } + + if (AR_SREV_9285(ah)) + ah->ah_txTrigLevel = (AR_FTRIG_256B >> AR_FTRIG_S); + else + ah->ah_txTrigLevel = (AR_FTRIG_512B >> AR_FTRIG_S); + +#ifndef ATH_NF_PER_CHAN + + ath9k_init_nfcal_hist_buffer(ah); +#endif + + return ah; + +bad: + if (ahp) + ath9k_hw_detach((struct ath_hal *) ahp); + if (status) + *status = ecode; + return NULL; +} + +void ath9k_hw_detach(struct ath_hal *ah) +{ + if (!AR_SREV_9100(ah)) + ath9k_hw_ani_detach(ah); + ath9k_hw_rfdetach(ah); + + ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); + kfree(ah); +} + +bool ath9k_get_channel_edges(struct ath_hal *ah, + u16 flags, u16 *low, + u16 *high) +{ + struct hal_capabilities *pCap = &ah->ah_caps; + + if (flags & CHANNEL_5GHZ) { + *low = pCap->halLow5GhzChan; + *high = pCap->halHigh5GhzChan; + return true; + } + if ((flags & CHANNEL_2GHZ)) { + *low = pCap->halLow2GhzChan; + *high = pCap->halHigh2GhzChan; + + return true; + } + return false; +} + +static inline bool ath9k_hw_fill_vpd_table(u8 pwrMin, + u8 pwrMax, + u8 *pPwrList, + u8 *pVpdList, + u16 + numIntercepts, + u8 *pRetVpdList) +{ + u16 i, k; + u8 currPwr = pwrMin; + u16 idxL = 0, idxR = 0; + + for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { + ath9k_hw_get_lower_upper_index(currPwr, pPwrList, + numIntercepts, &(idxL), + &(idxR)); + if (idxR < 1) + idxR = 1; + if (idxL == numIntercepts - 1) + idxL = (u16) (numIntercepts - 2); + if (pPwrList[idxL] == pPwrList[idxR]) + k = pVpdList[idxL]; + else + k = (u16) (((currPwr - + pPwrList[idxL]) * + pVpdList[idxR] + + (pPwrList[idxR] - + currPwr) * pVpdList[idxL]) / + (pPwrList[idxR] - + pPwrList[idxL])); + pRetVpdList[i] = (u8) k; + currPwr += 2; + } + + return true; +} + +static inline void +ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hal *ah, + struct ath9k_channel *chan, + struct cal_data_per_freq *pRawDataSet, + u8 *bChans, + u16 availPiers, + u16 tPdGainOverlap, + int16_t *pMinCalPower, + u16 *pPdGainBoundaries, + u8 *pPDADCValues, + u16 numXpdGains) +{ + int i, j, k; + int16_t ss; + u16 idxL = 0, idxR = 0, numPiers; + static u8 vpdTableL[AR5416_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + static u8 vpdTableR[AR5416_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + static u8 vpdTableI[AR5416_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR; + u8 minPwrT4[AR5416_NUM_PD_GAINS]; + u8 maxPwrT4[AR5416_NUM_PD_GAINS]; + int16_t vpdStep; + int16_t tmpVal; + u16 sizeCurrVpdTable, maxIndex, tgtIndex; + bool match; + int16_t minDelta = 0; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + for (numPiers = 0; numPiers < availPiers; numPiers++) { + if (bChans[numPiers] == AR5416_BCHAN_UNUSED) + break; + } + + match = ath9k_hw_get_lower_upper_index((u8) + FREQ2FBIN(centers. + synth_center, + IS_CHAN_2GHZ + (chan)), bChans, + numPiers, &idxL, &idxR); + + if (match) { + for (i = 0; i < numXpdGains; i++) { + minPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][0]; + maxPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][4]; + ath9k_hw_fill_vpd_table(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]; + + minPwrT4[i] = max(pPwrL[0], pPwrR[0]); + + maxPwrT4[i] = + min(pPwrL[AR5416_PD_GAIN_ICEPTS - 1], + pPwrR[AR5416_PD_GAIN_ICEPTS - 1]); + + + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pPwrL, pVpdL, + AR5416_PD_GAIN_ICEPTS, + vpdTableL[i]); + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pPwrR, pVpdR, + AR5416_PD_GAIN_ICEPTS, + vpdTableR[i]); + + for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) { + vpdTableI[i][j] = + (u8) (ath9k_hw_interpolate + ((u16) + FREQ2FBIN(centers. + synth_center, + IS_CHAN_2GHZ + (chan)), + bChans[idxL], + bChans[idxR], vpdTableL[i] + [j], vpdTableR[i] + [j])); + } + } + } + + *pMinCalPower = (int16_t) (minPwrT4[0] / 2); + + k = 0; + for (i = 0; i < numXpdGains; i++) { + if (i == (numXpdGains - 1)) + pPdGainBoundaries[i] = + (u16) (maxPwrT4[i] / 2); + else + pPdGainBoundaries[i] = + (u16) ((maxPwrT4[i] + + minPwrT4[i + 1]) / 4); + + pPdGainBoundaries[i] = + min((u16) AR5416_MAX_RATE_POWER, + pPdGainBoundaries[i]); + + if ((i == 0) && !AR_SREV_5416_V20_OR_LATER(ah)) { + minDelta = pPdGainBoundaries[0] - 23; + pPdGainBoundaries[0] = 23; + } else { + minDelta = 0; + } + + if (i == 0) { + if (AR_SREV_9280_10_OR_LATER(ah)) + ss = (int16_t) (0 - (minPwrT4[i] / 2)); + else + ss = 0; + } else { + 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); + + while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + tmpVal = (int16_t) (vpdTableI[i][0] + ss * vpdStep); + pPDADCValues[k++] = + (u8) ((tmpVal < 0) ? 0 : tmpVal); + ss++; + } + + sizeCurrVpdTable = + (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1); + tgtIndex = (u8) (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); + + if (tgtIndex > maxIndex) { + while ((ss <= tgtIndex) + && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + tmpVal = (int16_t) ((vpdTableI[i] + [sizeCurrVpdTable - + 1] + (ss - maxIndex + + 1) * vpdStep)); + pPDADCValues[k++] = (u8) ((tmpVal > + 255) ? 255 : tmpVal); + ss++; + } + } + } + + 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; +} + +static inline bool +ath9k_hw_set_power_cal_table(struct ath_hal *ah, + struct ar5416_eeprom *pEepData, + struct ath9k_channel *chan, + int16_t *pTxPowerIndexOffset) +{ + struct cal_data_per_freq *pRawDataset; + u8 *pCalBChans = NULL; + u16 pdGainOverlap_t2; + static u8 pdadcValues[AR5416_NUM_PDADC_VALUES]; + u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK]; + u16 numPiers, i, j; + int16_t tMinCalPower; + u16 numXpdGain, xpdMask; + u16 xpdGainValues[AR5416_NUM_PD_GAINS] = { 0, 0, 0, 0 }; + u32 reg32, regOffset, regChainOffset; + int16_t modalIdx; + struct ath_hal_5416 *ahp = AH5416(ah); + + modalIdx = IS_CHAN_2GHZ(chan) ? 1 : 0; + xpdMask = pEepData->modalHeader[modalIdx].xpdGain; + + if ((pEepData->baseEepHeader. + version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + pdGainOverlap_t2 = + pEepData->modalHeader[modalIdx].pdGainOverlap; + } else { + pdGainOverlap_t2 = + (u16) (MS + (REG_READ(ah, AR_PHY_TPCRG5), + AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); + } + + if (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; + + 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) + break; + xpdGainValues[numXpdGain] = + (u16) (AR5416_PD_GAINS_IN_MASK - i); + numXpdGain++; + } + } + + REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, + (numXpdGain - 1) & 0x3); + REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1, + xpdGainValues[0]); + REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2, + xpdGainValues[1]); + REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, + xpdGainValues[2]); + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + if (AR_SREV_5416_V20_OR_LATER(ah) && + (ahp->ah_rxchainmask == 5 || ahp->ah_txchainmask == 5) + && (i != 0)) { + regChainOffset = (i == 1) ? 0x2000 : 0x1000; + } else + regChainOffset = i * 0x1000; + if (pEepData->baseEepHeader.txMask & (1 << i)) { + if (IS_CHAN_2GHZ(chan)) + pRawDataset = pEepData->calPierData2G[i]; + else + pRawDataset = pEepData->calPierData5G[i]; + + ath9k_hw_get_gain_boundaries_pdadcs(ah, chan, + pRawDataset, + pCalBChans, + numPiers, + pdGainOverlap_t2, + &tMinCalPower, + gainBoundaries, + pdadcValues, + numXpdGain); + + if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) { + + 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)); + } + + 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); + REG_WRITE(ah, regOffset, reg32); + + DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO, + "PDADC (%d,%4x): %4.4x %8.8x\n", + i, regChainOffset, regOffset, + reg32); + DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO, + "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]); + + regOffset += 4; + } + } + } + *pTxPowerIndexOffset = 0; + + return true; +} + +void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u8 i; + + if (ah->ah_isPciExpress != true) + return; + + if (ah->ah_config.ath_hal_pciePowerSaveEnable == 2) + return; + + if (restore) + return; + + if (AR_SREV_9280_20_OR_LATER(ah)) { + for (i = 0; i < ahp->ah_iniPcieSerdes.ia_rows; i++) { + REG_WRITE(ah, INI_RA(&ahp->ah_iniPcieSerdes, i, 0), + INI_RA(&ahp->ah_iniPcieSerdes, i, 1)); + } + udelay(1000); + } else if (AR_SREV_9280(ah) + && (ah->ah_macRev == AR_SREV_REVISION_9280_10)) { + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + + REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019); + REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820); + REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560); + + if (ah->ah_config.ath_hal_pcieClockReq) + REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc); + else + REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd); + + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007); + + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + + udelay(1000); + } else { + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); + REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); + REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); + REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff); + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007); + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + } + + REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + + if (ah->ah_config.ath_hal_pcieWaen) { + REG_WRITE(ah, AR_WA, ah->ah_config.ath_hal_pcieWaen); + } else { + if (AR_SREV_9280(ah)) + REG_WRITE(ah, AR_WA, 0x0040073f); + else + REG_WRITE(ah, AR_WA, 0x0000073f); + } +} + +static inline void +ath9k_hw_get_legacy_target_powers(struct ath_hal *ah, + struct ath9k_channel *chan, + struct cal_target_power_leg *powInfo, + u16 numChannels, + struct cal_target_power_leg *pNewPower, + u16 numRates, + bool isExtTarget) +{ + u16 clo, chi; + int i; + int matchIndex = -1, lowIndex = -1; + u16 freq; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = (isExtTarget) ? centers.ext_center : centers.ctl_center; + + if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, + IS_CHAN_2GHZ(chan))) { + matchIndex = 0; + } else { + for (i = 0; (i < numChannels) + && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { + if (freq == + ath9k_hw_fbin2freq(powInfo[i].bChannel, + IS_CHAN_2GHZ(chan))) { + matchIndex = i; + break; + } else if ((freq < + ath9k_hw_fbin2freq(powInfo[i].bChannel, + IS_CHAN_2GHZ(chan))) + && (freq > + ath9k_hw_fbin2freq(powInfo[i - 1]. + bChannel, + IS_CHAN_2GHZ + (chan)))) { + lowIndex = i - 1; + break; + } + } + if ((matchIndex == -1) && (lowIndex == -1)) + matchIndex = i - 1; + } + + if (matchIndex != -1) { + *pNewPower = powInfo[matchIndex]; + } else { + clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, + IS_CHAN_2GHZ(chan)); + chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, + IS_CHAN_2GHZ(chan)); + + for (i = 0; i < numRates; i++) { + pNewPower->tPow2x[i] = + (u8) ath9k_hw_interpolate(freq, clo, chi, + powInfo + [lowIndex]. + tPow2x[i], + powInfo + [lowIndex + + 1].tPow2x[i]); + } + } +} + +static inline void +ath9k_hw_get_target_powers(struct ath_hal *ah, + struct ath9k_channel *chan, + struct cal_target_power_ht *powInfo, + u16 numChannels, + struct cal_target_power_ht *pNewPower, + u16 numRates, + bool isHt40Target) +{ + u16 clo, chi; + int i; + int matchIndex = -1, lowIndex = -1; + u16 freq; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = isHt40Target ? centers.synth_center : centers.ctl_center; + + if (freq <= + ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) { + matchIndex = 0; + } else { + for (i = 0; (i < numChannels) + && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { + if (freq == + ath9k_hw_fbin2freq(powInfo[i].bChannel, + IS_CHAN_2GHZ(chan))) { + matchIndex = i; + break; + } else + if ((freq < + ath9k_hw_fbin2freq(powInfo[i].bChannel, + IS_CHAN_2GHZ(chan))) + && (freq > + ath9k_hw_fbin2freq(powInfo[i - 1]. + bChannel, + IS_CHAN_2GHZ + (chan)))) { + lowIndex = i - 1; + break; + } + } + if ((matchIndex == -1) && (lowIndex == -1)) + matchIndex = i - 1; + } + + if (matchIndex != -1) { + *pNewPower = powInfo[matchIndex]; + } else { + clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, + IS_CHAN_2GHZ(chan)); + chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, + IS_CHAN_2GHZ(chan)); + + for (i = 0; i < numRates; i++) { + pNewPower->tPow2x[i] = + (u8) ath9k_hw_interpolate(freq, clo, chi, + powInfo + [lowIndex]. + tPow2x[i], + powInfo + [lowIndex + + 1].tPow2x[i]); + } + } +} + +static inline u16 +ath9k_hw_get_max_edge_power(u16 freq, + struct cal_ctl_edges *pRdEdgesPower, + bool is2GHz) +{ + u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + int i; + + for (i = 0; (i < AR5416_NUM_BAND_EDGES) + && (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) { + if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, + is2GHz)) { + twiceMaxEdgePower = pRdEdgesPower[i].tPower; + break; + } else if ((i > 0) + && (freq < + ath9k_hw_fbin2freq(pRdEdgesPower[i]. + bChannel, is2GHz))) { + if (ath9k_hw_fbin2freq + (pRdEdgesPower[i - 1].bChannel, is2GHz) < freq + && pRdEdgesPower[i - 1].flag) { + twiceMaxEdgePower = + pRdEdgesPower[i - 1].tPower; + } + break; + } + } + return twiceMaxEdgePower; +} + +static inline bool +ath9k_hw_set_power_per_rate_table(struct ath_hal *ah, + struct ar5416_eeprom *pEepData, + struct ath9k_channel *chan, + int16_t *ratesArray, + u16 cfgCtl, + u8 AntennaReduction, + u8 twiceMaxRegulatoryPower, + u8 powerLimit) +{ + u8 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + static const u16 tpScaleReductionTable[5] = + { 0, 3, 6, 9, AR5416_MAX_RATE_POWER }; + + int i; + int8_t twiceLargestAntenna; + struct cal_ctl_data *rep; + struct cal_target_power_leg targetPowerOfdm, targetPowerCck = { + 0, { 0, 0, 0, 0} + }; + struct cal_target_power_leg targetPowerOfdmExt = { + 0, { 0, 0, 0, 0} }, targetPowerCckExt = { + 0, { 0, 0, 0, 0 } + }; + struct cal_target_power_ht targetPowerHt20, targetPowerHt40 = { + 0, {0, 0, 0, 0} + }; + u8 scaledPower = 0, minCtlPower, maxRegAllowedPower; + u16 ctlModesFor11a[] = + { CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40 }; + u16 ctlModesFor11g[] = + { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, + CTL_2GHT40 + }; + u16 numCtlModes, *pCtlMode, ctlMode, freq; + struct chan_centers centers; + int tx_chainmask; + u8 twiceMinEdgePower; + struct ath_hal_5416 *ahp = AH5416(ah); + + tx_chainmask = ahp->ah_txchainmask; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + twiceLargestAntenna = max( + pEepData->modalHeader + [IS_CHAN_2GHZ(chan)].antennaGainCh[0], + pEepData->modalHeader + [IS_CHAN_2GHZ(chan)].antennaGainCh[1]); + + twiceLargestAntenna = max((u8) twiceLargestAntenna, + pEepData->modalHeader + [IS_CHAN_2GHZ(chan)].antennaGainCh[2]); + + twiceLargestAntenna = + (int8_t) min(AntennaReduction - twiceLargestAntenna, 0); + + maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna; + + if (ah->ah_tpScale != ATH9K_TP_SCALE_MAX) { + maxRegAllowedPower -= + (tpScaleReductionTable[(ah->ah_tpScale)] * 2); + } + + scaledPower = min(powerLimit, maxRegAllowedPower); + + switch (ar5416_get_ntxchains(tx_chainmask)) { + case 1: + break; + case 2: + scaledPower -= + pEepData->modalHeader[IS_CHAN_2GHZ(chan)]. + pwrDecreaseFor2Chain; + break; + case 3: + scaledPower -= + pEepData->modalHeader[IS_CHAN_2GHZ(chan)]. + pwrDecreaseFor3Chain; + break; + } + + scaledPower = max(0, (int32_t) scaledPower); + + if (IS_CHAN_2GHZ(chan)) { + numCtlModes = + ARRAY_SIZE(ctlModesFor11g) - + SUB_NUM_CTL_MODES_AT_2G_40; + pCtlMode = ctlModesFor11g; + + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPowerCck, + AR5416_NUM_2G_CCK_TARGET_POWERS, + &targetPowerCck, 4, + false); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPower2G, + AR5416_NUM_2G_20_TARGET_POWERS, + &targetPowerOfdm, 4, + false); + ath9k_hw_get_target_powers(ah, chan, + pEepData->calTargetPower2GHT20, + AR5416_NUM_2G_20_TARGET_POWERS, + &targetPowerHt20, 8, false); + + if (IS_CHAN_HT40(chan)) { + numCtlModes = ARRAY_SIZE(ctlModesFor11g); + ath9k_hw_get_target_powers(ah, chan, + pEepData-> + calTargetPower2GHT40, + AR5416_NUM_2G_40_TARGET_POWERS, + &targetPowerHt40, 8, + true); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPowerCck, + AR5416_NUM_2G_CCK_TARGET_POWERS, + &targetPowerCckExt, + 4, true); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPower2G, + AR5416_NUM_2G_20_TARGET_POWERS, + &targetPowerOfdmExt, + 4, true); + } + } else { + + numCtlModes = + ARRAY_SIZE(ctlModesFor11a) - + SUB_NUM_CTL_MODES_AT_5G_40; + pCtlMode = ctlModesFor11a; + + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPower5G, + AR5416_NUM_5G_20_TARGET_POWERS, + &targetPowerOfdm, 4, + false); + ath9k_hw_get_target_powers(ah, chan, + pEepData->calTargetPower5GHT20, + AR5416_NUM_5G_20_TARGET_POWERS, + &targetPowerHt20, 8, false); + + if (IS_CHAN_HT40(chan)) { + numCtlModes = ARRAY_SIZE(ctlModesFor11a); + ath9k_hw_get_target_powers(ah, chan, + pEepData-> + calTargetPower5GHT40, + AR5416_NUM_5G_40_TARGET_POWERS, + &targetPowerHt40, 8, + true); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPower5G, + AR5416_NUM_5G_20_TARGET_POWERS, + &targetPowerOfdmExt, + 4, true); + } + } + + for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { + bool isHt40CtlMode = + (pCtlMode[ctlMode] == CTL_5GHT40) + || (pCtlMode[ctlMode] == CTL_2GHT40); + if (isHt40CtlMode) + freq = centers.synth_center; + else if (pCtlMode[ctlMode] & EXT_ADDITIVE) + freq = centers.ext_center; + else + freq = centers.ctl_center; + + if (ar5416_get_eep_ver(ahp) == 14 + && ar5416_get_eep_rev(ahp) <= 2) + twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + "LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, " + "EXT_ADDITIVE %d\n", + ctlMode, numCtlModes, isHt40CtlMode, + (pCtlMode[ctlMode] & EXT_ADDITIVE)); + + for (i = 0; (i < AR5416_NUM_CTLS) && pEepData->ctlIndex[i]; + i++) { + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + " LOOP-Ctlidx %d: cfgCtl 0x%2.2x " + "pCtlMode 0x%2.2x ctlIndex 0x%2.2x " + "chan %d\n", + i, cfgCtl, pCtlMode[ctlMode], + pEepData->ctlIndex[i], chan->channel); + + 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 = + ath9k_hw_get_max_edge_power(freq, + rep-> + ctlEdges + [ar5416_get_ntxchains + (tx_chainmask) + - 1], + IS_CHAN_2GHZ + (chan)); + + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + " MATCH-EE_IDX %d: ch %d is2 %d " + "2xMinEdge %d chainmask %d chains %d\n", + i, freq, IS_CHAN_2GHZ(chan), + twiceMinEdgePower, tx_chainmask, + ar5416_get_ntxchains + (tx_chainmask)); + if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { + twiceMaxEdgePower = + min(twiceMaxEdgePower, + twiceMinEdgePower); + } else { + twiceMaxEdgePower = + twiceMinEdgePower; + break; + } + } + } + + minCtlPower = min(twiceMaxEdgePower, scaledPower); + + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + " SEL-Min ctlMode %d pCtlMode %d " + "2xMaxEdge %d sP %d minCtlPwr %d\n", + ctlMode, pCtlMode[ctlMode], twiceMaxEdgePower, + scaledPower, minCtlPower); + + switch (pCtlMode[ctlMode]) { + case CTL_11B: + for (i = 0; i < ARRAY_SIZE(targetPowerCck.tPow2x); + i++) { + targetPowerCck.tPow2x[i] = + min(targetPowerCck.tPow2x[i], + minCtlPower); + } + break; + case CTL_11A: + case CTL_11G: + for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); + i++) { + targetPowerOfdm.tPow2x[i] = + min(targetPowerOfdm.tPow2x[i], + minCtlPower); + } + break; + case CTL_5GHT20: + case CTL_2GHT20: + for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); + i++) { + targetPowerHt20.tPow2x[i] = + min(targetPowerHt20.tPow2x[i], + minCtlPower); + } + break; + case CTL_11B_EXT: + targetPowerCckExt.tPow2x[0] = + min(targetPowerCckExt.tPow2x[0], minCtlPower); + break; + case CTL_11A_EXT: + case CTL_11G_EXT: + targetPowerOfdmExt.tPow2x[0] = + min(targetPowerOfdmExt.tPow2x[0], minCtlPower); + break; + case CTL_5GHT40: + case CTL_2GHT40: + for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); + i++) { + targetPowerHt40.tPow2x[i] = + min(targetPowerHt40.tPow2x[i], + minCtlPower); + } + break; + default: + break; + } + } + + 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 < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) + ratesArray[rateHt20_0 + i] = targetPowerHt20.tPow2x[i]; + + if (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 (IS_CHAN_HT40(chan)) { + for (i = 0; i < ARRAY_SIZE(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 (IS_CHAN_2GHZ(chan)) { + ratesArray[rateExtCck] = + targetPowerCckExt.tPow2x[0]; + } + } + return true; +} + +static int +ath9k_hw_set_txpower(struct ath_hal *ah, + struct ar5416_eeprom *pEepData, + struct ath9k_channel *chan, + u16 cfgCtl, + u8 twiceAntennaReduction, + u8 twiceMaxRegulatoryPower, + u8 powerLimit) +{ + struct modal_eep_header *pModal = + &(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]); + int16_t ratesArray[Ar5416RateSize]; + int16_t txPowerIndexOffset = 0; + u8 ht40PowerIncForPdadc = 2; + int i; + + memset(ratesArray, 0, sizeof(ratesArray)); + + if ((pEepData->baseEepHeader. + version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; + } + + if (!ath9k_hw_set_power_per_rate_table(ah, pEepData, chan, + &ratesArray[0], cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, + powerLimit)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "ath9k_hw_set_txpower: unable to set " + "tx power per rate table\n"); + return -EIO; + } + + if (!ath9k_hw_set_power_cal_table + (ah, pEepData, chan, &txPowerIndexOffset)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "ath9k_hw_set_txpower: unable to set power table\n"); + return -EIO; + } + + for (i = 0; i < ARRAY_SIZE(ratesArray); i++) { + ratesArray[i] = + (int16_t) (txPowerIndexOffset + ratesArray[i]); + if (ratesArray[i] > AR5416_MAX_RATE_POWER) + ratesArray[i] = AR5416_MAX_RATE_POWER; + } + + if (AR_SREV_9280_10_OR_LATER(ah)) { + for (i = 0; i < Ar5416RateSize; i++) + ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2; + } + + REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, + ATH9K_POW_SM(ratesArray[rate18mb], 24) + | ATH9K_POW_SM(ratesArray[rate12mb], 16) + | ATH9K_POW_SM(ratesArray[rate9mb], 8) + | ATH9K_POW_SM(ratesArray[rate6mb], 0) + ); + REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, + ATH9K_POW_SM(ratesArray[rate54mb], 24) + | ATH9K_POW_SM(ratesArray[rate48mb], 16) + | ATH9K_POW_SM(ratesArray[rate36mb], 8) + | ATH9K_POW_SM(ratesArray[rate24mb], 0) + ); + + if (IS_CHAN_2GHZ(chan)) { + REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, + ATH9K_POW_SM(ratesArray[rate2s], 24) + | ATH9K_POW_SM(ratesArray[rate2l], 16) + | ATH9K_POW_SM(ratesArray[rateXr], 8) + | ATH9K_POW_SM(ratesArray[rate1l], 0) + ); + REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, + ATH9K_POW_SM(ratesArray[rate11s], 24) + | ATH9K_POW_SM(ratesArray[rate11l], 16) + | ATH9K_POW_SM(ratesArray[rate5_5s], 8) + | ATH9K_POW_SM(ratesArray[rate5_5l], 0) + ); + } + + REG_WRITE(ah, AR_PHY_POWER_TX_RATE5, + ATH9K_POW_SM(ratesArray[rateHt20_3], 24) + | ATH9K_POW_SM(ratesArray[rateHt20_2], 16) + | ATH9K_POW_SM(ratesArray[rateHt20_1], 8) + | ATH9K_POW_SM(ratesArray[rateHt20_0], 0) + ); + REG_WRITE(ah, AR_PHY_POWER_TX_RATE6, + ATH9K_POW_SM(ratesArray[rateHt20_7], 24) + | ATH9K_POW_SM(ratesArray[rateHt20_6], 16) + | ATH9K_POW_SM(ratesArray[rateHt20_5], 8) + | ATH9K_POW_SM(ratesArray[rateHt20_4], 0) + ); + + if (IS_CHAN_HT40(chan)) { + REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, + ATH9K_POW_SM(ratesArray[rateHt40_3] + + ht40PowerIncForPdadc, 24) + | ATH9K_POW_SM(ratesArray[rateHt40_2] + + ht40PowerIncForPdadc, 16) + | ATH9K_POW_SM(ratesArray[rateHt40_1] + + ht40PowerIncForPdadc, 8) + | ATH9K_POW_SM(ratesArray[rateHt40_0] + + ht40PowerIncForPdadc, 0) + ); + REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, + ATH9K_POW_SM(ratesArray[rateHt40_7] + + ht40PowerIncForPdadc, 24) + | ATH9K_POW_SM(ratesArray[rateHt40_6] + + ht40PowerIncForPdadc, 16) + | ATH9K_POW_SM(ratesArray[rateHt40_5] + + ht40PowerIncForPdadc, 8) + | ATH9K_POW_SM(ratesArray[rateHt40_4] + + ht40PowerIncForPdadc, 0) + ); + + REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, + ATH9K_POW_SM(ratesArray[rateExtOfdm], 24) + | ATH9K_POW_SM(ratesArray[rateExtCck], 16) + | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8) + | ATH9K_POW_SM(ratesArray[rateDupCck], 0) + ); + } + + REG_WRITE(ah, AR_PHY_POWER_TX_SUB, + ATH9K_POW_SM(pModal->pwrDecreaseFor3Chain, 6) + | ATH9K_POW_SM(pModal->pwrDecreaseFor2Chain, 0) + ); + + i = rate6mb; + if (IS_CHAN_HT40(chan)) + i = rateHt40_0; + else if (IS_CHAN_HT20(chan)) + i = rateHt20_0; + + if (AR_SREV_9280_10_OR_LATER(ah)) + ah->ah_maxPowerLevel = + ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2; + else + ah->ah_maxPowerLevel = ratesArray[i]; + + return 0; +} + +static inline void ath9k_hw_get_delta_slope_vals(struct ath_hal *ah, + u32 coef_scaled, + u32 *coef_mantissa, + u32 *coef_exponent) +{ + u32 coef_exp, coef_man; + + for (coef_exp = 31; coef_exp > 0; coef_exp--) + if ((coef_scaled >> coef_exp) & 0x1) + break; + + coef_exp = 14 - (coef_exp - COEF_SCALE_S); + + 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; +} + +static void +ath9k_hw_set_delta_slope(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + u32 coef_scaled, ds_coef_exp, ds_coef_man; + u32 clockMhzScaled = 0x64000000; + struct chan_centers centers; + + if (IS_CHAN_HALF_RATE(chan)) + clockMhzScaled = clockMhzScaled >> 1; + else if (IS_CHAN_QUARTER_RATE(chan)) + clockMhzScaled = clockMhzScaled >> 2; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + coef_scaled = clockMhzScaled / centers.synth_center; + + ath9k_hw_get_delta_slope_vals(ah, coef_scaled, &ds_coef_man, + &ds_coef_exp); + + REG_RMW_FIELD(ah, AR_PHY_TIMING3, + AR_PHY_TIMING3_DSC_MAN, ds_coef_man); + REG_RMW_FIELD(ah, AR_PHY_TIMING3, + AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); + + coef_scaled = (9 * coef_scaled) / 10; + + ath9k_hw_get_delta_slope_vals(ah, coef_scaled, &ds_coef_man, + &ds_coef_exp); + + REG_RMW_FIELD(ah, AR_PHY_HALFGI, + AR_PHY_HALFGI_DSC_MAN, ds_coef_man); + REG_RMW_FIELD(ah, AR_PHY_HALFGI, + AR_PHY_HALFGI_DSC_EXP, ds_coef_exp); +} + +static void ath9k_hw_9280_spur_mitigate(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + int bb_spur = AR_NO_SPUR; + int freq; + int bin, cur_bin; + int bb_spur_off, spur_subchannel_sd; + int spur_freq_sd; + int spur_delta_phase; + int denominator; + int upper, lower, cur_vit_mask; + int tmp, newVal; + int i; + int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, + AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 + }; + int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, + AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 + }; + int inc[4] = { 0, 100, 0, 0 }; + struct chan_centers centers; + + int8_t mask_m[123]; + int8_t mask_p[123]; + int8_t mask_amt; + int tmp_mask; + int cur_bb_spur; + bool is2GHz = IS_CHAN_2GHZ(chan); + + memset(&mask_m, 0, sizeof(int8_t) * 123); + memset(&mask_p, 0, sizeof(int8_t) * 123); + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = centers.synth_center; + + ah->ah_config.ath_hal_spurMode = SPUR_ENABLE_EEPROM; + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + cur_bb_spur = ath9k_hw_eeprom_get_spur_chan(ah, i, is2GHz); + + if (is2GHz) + cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_2GHZ; + else + cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_5GHZ; + + if (AR_NO_SPUR == cur_bb_spur) + break; + cur_bb_spur = cur_bb_spur - freq; + + if (IS_CHAN_HT40(chan)) { + if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT40) && + (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT40)) { + bb_spur = cur_bb_spur; + break; + } + } else if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT20) && + (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT20)) { + bb_spur = cur_bb_spur; + break; + } + } + + if (AR_NO_SPUR == bb_spur) { + REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, + AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); + return; + } else { + REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, + AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); + } + + bin = bb_spur * 320; + + tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0)); + + newVal = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | + AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | + AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | + AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); + REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), newVal); + + newVal = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | + AR_PHY_SPUR_REG_ENABLE_MASK_PPM | + AR_PHY_SPUR_REG_MASK_RATE_SELECT | + AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | + SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); + REG_WRITE(ah, AR_PHY_SPUR_REG, newVal); + + if (IS_CHAN_HT40(chan)) { + if (bb_spur < 0) { + spur_subchannel_sd = 1; + bb_spur_off = bb_spur + 10; + } else { + spur_subchannel_sd = 0; + bb_spur_off = bb_spur - 10; + } + } else { + spur_subchannel_sd = 0; + bb_spur_off = bb_spur; + } + + if (IS_CHAN_HT40(chan)) + spur_delta_phase = + ((bb_spur * 262144) / + 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; + else + spur_delta_phase = + ((bb_spur * 524288) / + 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; + + denominator = IS_CHAN_2GHZ(chan) ? 44 : 40; + spur_freq_sd = ((bb_spur_off * 2048) / denominator) & 0x3ff; + + newVal = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | + SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | + SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); + REG_WRITE(ah, AR_PHY_TIMING11, newVal); + + newVal = spur_subchannel_sd << AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S; + REG_WRITE(ah, AR_PHY_SFCORR_EXT, newVal); + + cur_bin = -6000; + upper = bin + 100; + lower = bin - 100; + + for (i = 0; i < 4; i++) { + int pilot_mask = 0; + int chan_mask = 0; + int bp = 0; + for (bp = 0; bp < 30; bp++) { + if ((cur_bin > lower) && (cur_bin < upper)) { + pilot_mask = pilot_mask | 0x1 << bp; + chan_mask = chan_mask | 0x1 << bp; + } + cur_bin += 100; + } + cur_bin += inc[i]; + REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); + REG_WRITE(ah, chan_mask_reg[i], chan_mask); + } + + cur_vit_mask = 6100; + upper = bin + 120; + lower = bin - 120; + + for (i = 0; i < 123; i++) { + if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { + if ((abs(cur_vit_mask - bin)) < 75) + mask_amt = 1; + else + mask_amt = 0; + if (cur_vit_mask < 0) + mask_m[abs(cur_vit_mask / 100)] = mask_amt; + else + mask_p[cur_vit_mask / 100] = mask_amt; + } + cur_vit_mask -= 100; + } + + tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) + | (mask_m[48] << 26) | (mask_m[49] << 24) + | (mask_m[50] << 22) | (mask_m[51] << 20) + | (mask_m[52] << 18) | (mask_m[53] << 16) + | (mask_m[54] << 14) | (mask_m[55] << 12) + | (mask_m[56] << 10) | (mask_m[57] << 8) + | (mask_m[58] << 6) | (mask_m[59] << 4) + | (mask_m[60] << 2) | (mask_m[61] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); + REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); + + tmp_mask = (mask_m[31] << 28) + | (mask_m[32] << 26) | (mask_m[33] << 24) + | (mask_m[34] << 22) | (mask_m[35] << 20) + | (mask_m[36] << 18) | (mask_m[37] << 16) + | (mask_m[48] << 14) | (mask_m[39] << 12) + | (mask_m[40] << 10) | (mask_m[41] << 8) + | (mask_m[42] << 6) | (mask_m[43] << 4) + | (mask_m[44] << 2) | (mask_m[45] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); + + tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) + | (mask_m[18] << 26) | (mask_m[18] << 24) + | (mask_m[20] << 22) | (mask_m[20] << 20) + | (mask_m[22] << 18) | (mask_m[22] << 16) + | (mask_m[24] << 14) | (mask_m[24] << 12) + | (mask_m[25] << 10) | (mask_m[26] << 8) + | (mask_m[27] << 6) | (mask_m[28] << 4) + | (mask_m[29] << 2) | (mask_m[30] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); + + tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28) + | (mask_m[2] << 26) | (mask_m[3] << 24) + | (mask_m[4] << 22) | (mask_m[5] << 20) + | (mask_m[6] << 18) | (mask_m[7] << 16) + | (mask_m[8] << 14) | (mask_m[9] << 12) + | (mask_m[10] << 10) | (mask_m[11] << 8) + | (mask_m[12] << 6) | (mask_m[13] << 4) + | (mask_m[14] << 2) | (mask_m[15] << 0); + REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); + + tmp_mask = (mask_p[15] << 28) + | (mask_p[14] << 26) | (mask_p[13] << 24) + | (mask_p[12] << 22) | (mask_p[11] << 20) + | (mask_p[10] << 18) | (mask_p[9] << 16) + | (mask_p[8] << 14) | (mask_p[7] << 12) + | (mask_p[6] << 10) | (mask_p[5] << 8) + | (mask_p[4] << 6) | (mask_p[3] << 4) + | (mask_p[2] << 2) | (mask_p[1] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); + + tmp_mask = (mask_p[30] << 28) + | (mask_p[29] << 26) | (mask_p[28] << 24) + | (mask_p[27] << 22) | (mask_p[26] << 20) + | (mask_p[25] << 18) | (mask_p[24] << 16) + | (mask_p[23] << 14) | (mask_p[22] << 12) + | (mask_p[21] << 10) | (mask_p[20] << 8) + | (mask_p[19] << 6) | (mask_p[18] << 4) + | (mask_p[17] << 2) | (mask_p[16] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); + + tmp_mask = (mask_p[45] << 28) + | (mask_p[44] << 26) | (mask_p[43] << 24) + | (mask_p[42] << 22) | (mask_p[41] << 20) + | (mask_p[40] << 18) | (mask_p[39] << 16) + | (mask_p[38] << 14) | (mask_p[37] << 12) + | (mask_p[36] << 10) | (mask_p[35] << 8) + | (mask_p[34] << 6) | (mask_p[33] << 4) + | (mask_p[32] << 2) | (mask_p[31] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); + + tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) + | (mask_p[59] << 26) | (mask_p[58] << 24) + | (mask_p[57] << 22) | (mask_p[56] << 20) + | (mask_p[55] << 18) | (mask_p[54] << 16) + | (mask_p[53] << 14) | (mask_p[52] << 12) + | (mask_p[51] << 10) | (mask_p[50] << 8) + | (mask_p[49] << 6) | (mask_p[48] << 4) + | (mask_p[47] << 2) | (mask_p[46] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); +} + +static void ath9k_hw_spur_mitigate(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + int bb_spur = AR_NO_SPUR; + int bin, cur_bin; + int spur_freq_sd; + int spur_delta_phase; + int denominator; + int upper, lower, cur_vit_mask; + int tmp, new; + int i; + int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, + AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 + }; + int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, + AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 + }; + int inc[4] = { 0, 100, 0, 0 }; + + int8_t mask_m[123]; + int8_t mask_p[123]; + int8_t mask_amt; + int tmp_mask; + int cur_bb_spur; + bool is2GHz = IS_CHAN_2GHZ(chan); + + memset(&mask_m, 0, sizeof(int8_t) * 123); + memset(&mask_p, 0, sizeof(int8_t) * 123); + + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + cur_bb_spur = ath9k_hw_eeprom_get_spur_chan(ah, i, is2GHz); + if (AR_NO_SPUR == cur_bb_spur) + break; + cur_bb_spur = cur_bb_spur - (chan->channel * 10); + if ((cur_bb_spur > -95) && (cur_bb_spur < 95)) { + bb_spur = cur_bb_spur; + break; + } + } + + if (AR_NO_SPUR == bb_spur) + return; + + bin = bb_spur * 32; + + tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0)); + new = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | + AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | + AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | + AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); + + REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), new); + + new = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | + AR_PHY_SPUR_REG_ENABLE_MASK_PPM | + AR_PHY_SPUR_REG_MASK_RATE_SELECT | + AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | + SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); + REG_WRITE(ah, AR_PHY_SPUR_REG, new); + + spur_delta_phase = ((bb_spur * 524288) / 100) & + AR_PHY_TIMING11_SPUR_DELTA_PHASE; + + denominator = IS_CHAN_2GHZ(chan) ? 440 : 400; + spur_freq_sd = ((bb_spur * 2048) / denominator) & 0x3ff; + + new = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | + SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | + SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); + REG_WRITE(ah, AR_PHY_TIMING11, new); + + cur_bin = -6000; + upper = bin + 100; + lower = bin - 100; + + for (i = 0; i < 4; i++) { + int pilot_mask = 0; + int chan_mask = 0; + int bp = 0; + for (bp = 0; bp < 30; bp++) { + if ((cur_bin > lower) && (cur_bin < upper)) { + pilot_mask = pilot_mask | 0x1 << bp; + chan_mask = chan_mask | 0x1 << bp; + } + cur_bin += 100; + } + cur_bin += inc[i]; + REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); + REG_WRITE(ah, chan_mask_reg[i], chan_mask); + } + + cur_vit_mask = 6100; + upper = bin + 120; + lower = bin - 120; + + for (i = 0; i < 123; i++) { + if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { + if ((abs(cur_vit_mask - bin)) < 75) + mask_amt = 1; + else + mask_amt = 0; + if (cur_vit_mask < 0) + mask_m[abs(cur_vit_mask / 100)] = mask_amt; + else + mask_p[cur_vit_mask / 100] = mask_amt; + } + cur_vit_mask -= 100; + } + + tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) + | (mask_m[48] << 26) | (mask_m[49] << 24) + | (mask_m[50] << 22) | (mask_m[51] << 20) + | (mask_m[52] << 18) | (mask_m[53] << 16) + | (mask_m[54] << 14) | (mask_m[55] << 12) + | (mask_m[56] << 10) | (mask_m[57] << 8) + | (mask_m[58] << 6) | (mask_m[59] << 4) + | (mask_m[60] << 2) | (mask_m[61] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); + REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); + + tmp_mask = (mask_m[31] << 28) + | (mask_m[32] << 26) | (mask_m[33] << 24) + | (mask_m[34] << 22) | (mask_m[35] << 20) + | (mask_m[36] << 18) | (mask_m[37] << 16) + | (mask_m[48] << 14) | (mask_m[39] << 12) + | (mask_m[40] << 10) | (mask_m[41] << 8) + | (mask_m[42] << 6) | (mask_m[43] << 4) + | (mask_m[44] << 2) | (mask_m[45] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); + + tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) + | (mask_m[18] << 26) | (mask_m[18] << 24) + | (mask_m[20] << 22) | (mask_m[20] << 20) + | (mask_m[22] << 18) | (mask_m[22] << 16) + | (mask_m[24] << 14) | (mask_m[24] << 12) + | (mask_m[25] << 10) | (mask_m[26] << 8) + | (mask_m[27] << 6) | (mask_m[28] << 4) + | (mask_m[29] << 2) | (mask_m[30] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); + + tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28) + | (mask_m[2] << 26) | (mask_m[3] << 24) + | (mask_m[4] << 22) | (mask_m[5] << 20) + | (mask_m[6] << 18) | (mask_m[7] << 16) + | (mask_m[8] << 14) | (mask_m[9] << 12) + | (mask_m[10] << 10) | (mask_m[11] << 8) + | (mask_m[12] << 6) | (mask_m[13] << 4) + | (mask_m[14] << 2) | (mask_m[15] << 0); + REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); + + tmp_mask = (mask_p[15] << 28) + | (mask_p[14] << 26) | (mask_p[13] << 24) + | (mask_p[12] << 22) | (mask_p[11] << 20) + | (mask_p[10] << 18) | (mask_p[9] << 16) + | (mask_p[8] << 14) | (mask_p[7] << 12) + | (mask_p[6] << 10) | (mask_p[5] << 8) + | (mask_p[4] << 6) | (mask_p[3] << 4) + | (mask_p[2] << 2) | (mask_p[1] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); + + tmp_mask = (mask_p[30] << 28) + | (mask_p[29] << 26) | (mask_p[28] << 24) + | (mask_p[27] << 22) | (mask_p[26] << 20) + | (mask_p[25] << 18) | (mask_p[24] << 16) + | (mask_p[23] << 14) | (mask_p[22] << 12) + | (mask_p[21] << 10) | (mask_p[20] << 8) + | (mask_p[19] << 6) | (mask_p[18] << 4) + | (mask_p[17] << 2) | (mask_p[16] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); + + tmp_mask = (mask_p[45] << 28) + | (mask_p[44] << 26) | (mask_p[43] << 24) + | (mask_p[42] << 22) | (mask_p[41] << 20) + | (mask_p[40] << 18) | (mask_p[39] << 16) + | (mask_p[38] << 14) | (mask_p[37] << 12) + | (mask_p[36] << 10) | (mask_p[35] << 8) + | (mask_p[34] << 6) | (mask_p[33] << 4) + | (mask_p[32] << 2) | (mask_p[31] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); + + tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) + | (mask_p[59] << 26) | (mask_p[58] << 24) + | (mask_p[57] << 22) | (mask_p[56] << 20) + | (mask_p[55] << 18) | (mask_p[54] << 16) + | (mask_p[53] << 14) | (mask_p[52] << 12) + | (mask_p[51] << 10) | (mask_p[50] << 8) + | (mask_p[49] << 6) | (mask_p[48] << 4) + | (mask_p[47] << 2) | (mask_p[46] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); +} + +static inline void ath9k_hw_init_chain_masks(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int rx_chainmask, tx_chainmask; + + rx_chainmask = ahp->ah_rxchainmask; + tx_chainmask = ahp->ah_txchainmask; + + switch (rx_chainmask) { + case 0x5: + REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, + AR_PHY_SWAP_ALT_CHAIN); + case 0x3: + if (((ah)->ah_macVersion <= AR_SREV_VERSION_9160)) { + REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7); + REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7); + break; + } + case 0x1: + case 0x2: + if (!AR_SREV_9280(ah)) + break; + case 0x7: + REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); + REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); + break; + default: + break; + } + + REG_WRITE(ah, AR_SELFGEN_MASK, tx_chainmask); + if (tx_chainmask == 0x5) { + REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, + AR_PHY_SWAP_ALT_CHAIN); + } + if (AR_SREV_9100(ah)) + REG_WRITE(ah, AR_PHY_ANALOG_SWAP, + REG_READ(ah, AR_PHY_ANALOG_SWAP) | 0x00000001); +} + +static void ath9k_hw_set_addac(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_header *pModal; + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + u8 biaslevel; + + if (ah->ah_macVersion != AR_SREV_VERSION_9160) + return; + + if (ar5416_get_eep_rev(ahp) < AR5416_EEP_MINOR_VER_7) + return; + + pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); + + if (pModal->xpaBiasLvl != 0xff) { + biaslevel = pModal->xpaBiasLvl; + } else { + + u16 resetFreqBin, freqBin, freqCount = 0; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + resetFreqBin = + FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan)); + freqBin = pModal->xpaBiasLvlFreq[0] & 0xff; + biaslevel = (u8) (pModal->xpaBiasLvlFreq[0] >> 14); + + freqCount++; + + while (freqCount < 3) { + if (pModal->xpaBiasLvlFreq[freqCount] == 0x0) + break; + + freqBin = pModal->xpaBiasLvlFreq[freqCount] & 0xff; + if (resetFreqBin >= freqBin) { + biaslevel = + (u8) (pModal-> + xpaBiasLvlFreq[freqCount] + >> 14); + } else { + break; + } + freqCount++; + } + } + + if (IS_CHAN_2GHZ(chan)) { + INI_RA(&ahp->ah_iniAddac, 7, 1) = + (INI_RA(&ahp->ah_iniAddac, 7, 1) & (~0x18)) | biaslevel + << 3; + } else { + INI_RA(&ahp->ah_iniAddac, 6, 1) = + (INI_RA(&ahp->ah_iniAddac, 6, 1) & (~0xc0)) | biaslevel + << 6; + } +} + +static u32 ath9k_hw_mac_usec(struct ath_hal *ah, u32 clks) +{ + if (ah->ah_curchan != NULL) + return clks / + CLOCK_RATE[ath9k_hw_chan2wmode(ah, ah->ah_curchan)]; + else + return clks / CLOCK_RATE[WIRELESS_MODE_11b]; +} + +static u32 ath9k_hw_mac_to_usec(struct ath_hal *ah, u32 clks) +{ + struct ath9k_channel *chan = ah->ah_curchan; + + if (chan && IS_CHAN_HT40(chan)) + return ath9k_hw_mac_usec(ah, clks) / 2; + else + return ath9k_hw_mac_usec(ah, clks); +} + +static u32 ath9k_hw_mac_clks(struct ath_hal *ah, u32 usecs) +{ + if (ah->ah_curchan != NULL) + return usecs * CLOCK_RATE[ath9k_hw_chan2wmode(ah, + ah->ah_curchan)]; + else + return usecs * CLOCK_RATE[WIRELESS_MODE_11b]; +} + +static u32 ath9k_hw_mac_to_clks(struct ath_hal *ah, u32 usecs) +{ + struct ath9k_channel *chan = ah->ah_curchan; + + if (chan && IS_CHAN_HT40(chan)) + return ath9k_hw_mac_clks(ah, usecs) * 2; + else + return ath9k_hw_mac_clks(ah, usecs); +} + +static bool ath9k_hw_set_ack_timeout(struct ath_hal *ah, u32 us) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: bad ack timeout %u\n", + __func__, us); + ahp->ah_acktimeout = (u32) -1; + return false; + } else { + REG_RMW_FIELD(ah, AR_TIME_OUT, + AR_TIME_OUT_ACK, ath9k_hw_mac_to_clks(ah, us)); + ahp->ah_acktimeout = us; + return true; + } +} + +static bool ath9k_hw_set_cts_timeout(struct ath_hal *ah, u32 us) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: bad cts timeout %u\n", + __func__, us); + ahp->ah_ctstimeout = (u32) -1; + return false; + } else { + REG_RMW_FIELD(ah, AR_TIME_OUT, + AR_TIME_OUT_CTS, ath9k_hw_mac_to_clks(ah, us)); + ahp->ah_ctstimeout = us; + return true; + } +} +static bool ath9k_hw_set_global_txtimeout(struct ath_hal *ah, + u32 tu) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (tu > 0xFFFF) { + DPRINTF(ah->ah_sc, ATH_DBG_XMIT, + "%s: bad global tx timeout %u\n", __func__, tu); + ahp->ah_globaltxtimeout = (u32) -1; + return false; + } else { + REG_RMW_FIELD(ah, AR_GTXTO, AR_GTXTO_TIMEOUT_LIMIT, tu); + ahp->ah_globaltxtimeout = tu; + return true; + } +} + +bool ath9k_hw_setslottime(struct ath_hal *ah, u32 us) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (us < ATH9K_SLOT_TIME_9 || us > ath9k_hw_mac_to_usec(ah, 0xffff)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: bad slot time %u\n", + __func__, us); + ahp->ah_slottime = (u32) -1; + return false; + } else { + REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath9k_hw_mac_to_clks(ah, us)); + ahp->ah_slottime = us; + return true; + } +} + +static inline void ath9k_hw_init_user_settings(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "--AP %s ahp->ah_miscMode 0x%x\n", + __func__, ahp->ah_miscMode); + if (ahp->ah_miscMode != 0) + REG_WRITE(ah, AR_PCU_MISC, + REG_READ(ah, AR_PCU_MISC) | ahp->ah_miscMode); + if (ahp->ah_slottime != (u32) -1) + ath9k_hw_setslottime(ah, ahp->ah_slottime); + if (ahp->ah_acktimeout != (u32) -1) + ath9k_hw_set_ack_timeout(ah, ahp->ah_acktimeout); + if (ahp->ah_ctstimeout != (u32) -1) + ath9k_hw_set_cts_timeout(ah, ahp->ah_ctstimeout); + if (ahp->ah_globaltxtimeout != (u32) -1) + ath9k_hw_set_global_txtimeout(ah, ahp->ah_globaltxtimeout); +} + +static inline int +ath9k_hw_process_ini(struct ath_hal *ah, + struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode) +{ + int i, regWrites = 0; + struct ath_hal_5416 *ahp = AH5416(ah); + u32 modesIndex, freqIndex; + int status; + + switch (chan->chanmode) { + case CHANNEL_A: + case CHANNEL_A_HT20: + modesIndex = 1; + freqIndex = 1; + break; + case CHANNEL_A_HT40PLUS: + case CHANNEL_A_HT40MINUS: + modesIndex = 2; + freqIndex = 1; + break; + case CHANNEL_G: + case CHANNEL_G_HT20: + case CHANNEL_B: + modesIndex = 4; + freqIndex = 2; + break; + case CHANNEL_G_HT40PLUS: + case CHANNEL_G_HT40MINUS: + modesIndex = 3; + freqIndex = 2; + break; + + default: + return -EINVAL; + } + + REG_WRITE(ah, AR_PHY(0), 0x00000007); + + REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO); + + ath9k_hw_set_addac(ah, chan); + + if (AR_SREV_5416_V22_OR_LATER(ah)) { + REG_WRITE_ARRAY(&ahp->ah_iniAddac, 1, regWrites); + } else { + struct ar5416IniArray temp; + u32 addacSize = + sizeof(u32) * ahp->ah_iniAddac.ia_rows * + ahp->ah_iniAddac.ia_columns; + + memcpy(ahp->ah_addac5416_21, + ahp->ah_iniAddac.ia_array, addacSize); + + (ahp->ah_addac5416_21)[31 * + ahp->ah_iniAddac.ia_columns + 1] = 0; + + temp.ia_array = ahp->ah_addac5416_21; + temp.ia_columns = ahp->ah_iniAddac.ia_columns; + temp.ia_rows = ahp->ah_iniAddac.ia_rows; + REG_WRITE_ARRAY(&temp, 1, regWrites); + } + REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); + + for (i = 0; i < ahp->ah_iniModes.ia_rows; i++) { + u32 reg = INI_RA(&ahp->ah_iniModes, i, 0); + u32 val = INI_RA(&ahp->ah_iniModes, i, modesIndex); + +#ifdef CONFIG_SLOW_ANT_DIV + if (ah->ah_devid == AR9280_DEVID_PCI) + val = ath9k_hw_ini_fixup(ah, &ahp->ah_eeprom, reg, + val); +#endif + + REG_WRITE(ah, reg, val); + + if (reg >= 0x7800 && reg < 0x78a0 + && ah->ah_config.ath_hal_analogShiftReg) { + udelay(100); + } + + DO_DELAY(regWrites); + } + + for (i = 0; i < ahp->ah_iniCommon.ia_rows; i++) { + u32 reg = INI_RA(&ahp->ah_iniCommon, i, 0); + u32 val = INI_RA(&ahp->ah_iniCommon, i, 1); + + REG_WRITE(ah, reg, val); + + if (reg >= 0x7800 && reg < 0x78a0 + && ah->ah_config.ath_hal_analogShiftReg) { + udelay(100); + } + + DO_DELAY(regWrites); + } + + ath9k_hw_write_regs(ah, modesIndex, freqIndex, regWrites); + + if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) { + REG_WRITE_ARRAY(&ahp->ah_iniModesAdditional, modesIndex, + regWrites); + } + + ath9k_hw_override_ini(ah, chan); + ath9k_hw_set_regs(ah, chan, macmode); + ath9k_hw_init_chain_masks(ah); + + status = ath9k_hw_set_txpower(ah, &ahp->ah_eeprom, chan, + ath9k_regd_get_ctl(ah, chan), + ath9k_regd_get_antenna_allowed(ah, + chan), + chan->maxRegTxPower * 2, + min((u32) MAX_RATE_POWER, + (u32) ah->ah_powerLimit)); + if (status != 0) { + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + "%s: error init'ing transmit power\n", __func__); + return -EIO; + } + + if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "%s: ar5416SetRfRegs failed\n", __func__); + return -EIO; + } + + return 0; +} + +static inline void ath9k_hw_setup_calibration(struct ath_hal *ah, + struct hal_cal_list *currCal) +{ + REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(0), + AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX, + currCal->calData->calCountMax); + + switch (currCal->calData->calType) { + case IQ_MISMATCH_CAL: + REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: starting IQ Mismatch Calibration\n", + __func__); + break; + case ADC_GAIN_CAL: + REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: starting ADC Gain Calibration\n", __func__); + break; + case ADC_DC_CAL: + REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: starting ADC DC Calibration\n", __func__); + break; + case ADC_DC_INIT_CAL: + REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: starting Init ADC DC Calibration\n", + __func__); + break; + } + + REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0), + AR_PHY_TIMING_CTRL4_DO_CAL); +} + +static inline void ath9k_hw_reset_calibration(struct ath_hal *ah, + struct hal_cal_list *currCal) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + ath9k_hw_setup_calibration(ah, currCal); + + currCal->calState = CAL_RUNNING; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ahp->ah_Meas0.sign[i] = 0; + ahp->ah_Meas1.sign[i] = 0; + ahp->ah_Meas2.sign[i] = 0; + ahp->ah_Meas3.sign[i] = 0; + } + + ahp->ah_CalSamples = 0; +} + +static inline void +ath9k_hw_per_calibration(struct ath_hal *ah, + struct ath9k_channel *ichan, + u8 rxchainmask, + struct hal_cal_list *currCal, + bool *isCalDone) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + *isCalDone = false; + + if (currCal->calState == CAL_RUNNING) { + if (!(REG_READ(ah, + AR_PHY_TIMING_CTRL4(0)) & + AR_PHY_TIMING_CTRL4_DO_CAL)) { + + currCal->calData->calCollect(ah); + + ahp->ah_CalSamples++; + + if (ahp->ah_CalSamples >= + currCal->calData->calNumSamples) { + int i, numChains = 0; + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + if (rxchainmask & (1 << i)) + numChains++; + } + + currCal->calData->calPostProc(ah, + numChains); + + ichan->CalValid |= + currCal->calData->calType; + currCal->calState = CAL_DONE; + *isCalDone = true; + } else { + ath9k_hw_setup_calibration(ah, currCal); + } + } + } else if (!(ichan->CalValid & currCal->calData->calType)) { + ath9k_hw_reset_calibration(ah, currCal); + } +} + +static inline bool ath9k_hw_run_init_cals(struct ath_hal *ah, + int init_cal_count) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel ichan; + bool isCalDone; + struct hal_cal_list *currCal = ahp->ah_cal_list_curr; + const struct hal_percal_data *calData = currCal->calData; + int i; + + if (currCal == NULL) + return false; + + ichan.CalValid = 0; + + for (i = 0; i < init_cal_count; i++) { + ath9k_hw_reset_calibration(ah, currCal); + + if (!ath9k_hw_wait(ah, AR_PHY_TIMING_CTRL4(0), + AR_PHY_TIMING_CTRL4_DO_CAL, 0)) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: Cal %d failed to complete in 100ms.\n", + __func__, calData->calType); + + ahp->ah_cal_list = ahp->ah_cal_list_last = + ahp->ah_cal_list_curr = NULL; + return false; + } + + ath9k_hw_per_calibration(ah, &ichan, ahp->ah_rxchainmask, + currCal, &isCalDone); + if (!isCalDone) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: Not able to run Init Cal %d.\n", + __func__, calData->calType); + } + if (currCal->calNext) { + currCal = currCal->calNext; + calData = currCal->calData; + } + } + + ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = NULL; + return true; +} + +static inline bool +ath9k_hw_channel_change(struct ath_hal *ah, + struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode) +{ + u32 synthDelay, qnum; + struct ath_hal_5416 *ahp = AH5416(ah); + + for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { + if (ath9k_hw_numtxpending(ah, qnum)) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: Transmit frames pending on queue %d\n", + __func__, qnum); + return false; + } + } + + REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN); + if (!ath9k_hw_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN, + AR_PHY_RFBUS_GRANT_EN)) { + DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO, + "%s: Could not kill baseband RX\n", __func__); + return false; + } + + ath9k_hw_set_regs(ah, chan, macmode); + + if (AR_SREV_9280_10_OR_LATER(ah)) { + if (!(ath9k_hw_ar9280_set_channel(ah, chan))) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: failed to set channel\n", __func__); + return false; + } + } else { + if (!(ath9k_hw_set_channel(ah, chan))) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: failed to set channel\n", __func__); + return false; + } + } + + if (ath9k_hw_set_txpower(ah, &ahp->ah_eeprom, chan, + ath9k_regd_get_ctl(ah, chan), + ath9k_regd_get_antenna_allowed(ah, chan), + chan->maxRegTxPower * 2, + min((u32) MAX_RATE_POWER, + (u32) ah->ah_powerLimit)) != 0) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: error init'ing transmit power\n", __func__); + return false; + } + + synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; + if (IS_CHAN_CCK(chan)) + synthDelay = (4 * synthDelay) / 22; + else + synthDelay /= 10; + + udelay(synthDelay + BASE_ACTIVATE_DELAY); + + REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); + + if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) + ath9k_hw_set_delta_slope(ah, chan); + + if (AR_SREV_9280_10_OR_LATER(ah)) + ath9k_hw_9280_spur_mitigate(ah, chan); + else + ath9k_hw_spur_mitigate(ah, chan); + + if (!chan->oneTimeCalsDone) + chan->oneTimeCalsDone = true; + + return true; +} + +static bool ath9k_hw_chip_reset(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM)) + return false; + + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + return false; + + ahp->ah_chipFullSleep = false; + + ath9k_hw_init_pll(ah, chan); + + ath9k_hw_set_rfmode(ah, chan); + + return true; +} + +static inline void ath9k_hw_set_dma(struct ath_hal *ah) +{ + u32 regval; + + regval = REG_READ(ah, AR_AHB_MODE); + REG_WRITE(ah, AR_AHB_MODE, regval | AR_AHB_PREFETCH_RD_EN); + + regval = REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK; + REG_WRITE(ah, AR_TXCFG, regval | AR_TXCFG_DMASZ_128B); + + REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, ah->ah_txTrigLevel); + + regval = REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK; + REG_WRITE(ah, AR_RXCFG, regval | AR_RXCFG_DMASZ_128B); + + REG_WRITE(ah, AR_RXFIFO_CFG, 0x200); + + if (AR_SREV_9285(ah)) { + REG_WRITE(ah, AR_PCU_TXBUF_CTRL, + AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE); + } else { + REG_WRITE(ah, AR_PCU_TXBUF_CTRL, + AR_PCU_TXBUF_CTRL_USABLE_SIZE); + } +} + +bool ath9k_hw_stopdmarecv(struct ath_hal *ah) +{ + REG_WRITE(ah, AR_CR, AR_CR_RXD); + if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0)) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: dma failed to stop in 10ms\n" + "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n", + __func__, + REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW)); + return false; + } else { + return true; + } +} + +void ath9k_hw_startpcureceive(struct ath_hal *ah) +{ + REG_CLR_BIT(ah, AR_DIAG_SW, + (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + + ath9k_enable_mib_counters(ah); + + ath9k_ani_reset(ah); +} + +void ath9k_hw_stoppcurecv(struct ath_hal *ah) +{ + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + + ath9k_hw_disable_mib_counters(ah); +} + +static bool ath9k_hw_iscal_supported(struct ath_hal *ah, + struct ath9k_channel *chan, + enum hal_cal_types calType) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + bool retval = false; + + switch (calType & ahp->ah_suppCals) { + case IQ_MISMATCH_CAL: + if (!IS_CHAN_B(chan)) + retval = true; + break; + case ADC_GAIN_CAL: + case ADC_DC_CAL: + if (!IS_CHAN_B(chan) + && !(IS_CHAN_2GHZ(chan) && IS_CHAN_HT20(chan))) + retval = true; + break; + } + + return retval; +} + +static inline bool ath9k_hw_init_cal(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *ichan = + ath9k_regd_check_channel(ah, chan); + + REG_WRITE(ah, AR_PHY_AGC_CONTROL, + REG_READ(ah, AR_PHY_AGC_CONTROL) | + AR_PHY_AGC_CONTROL_CAL); + + if (!ath9k_hw_wait + (ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: offset calibration failed to complete in 1ms; " + "noisy environment?\n", __func__); + return false; + } + + REG_WRITE(ah, AR_PHY_AGC_CONTROL, + REG_READ(ah, AR_PHY_AGC_CONTROL) | + AR_PHY_AGC_CONTROL_NF); + + ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = + NULL; + + if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) { + if (ath9k_hw_iscal_supported(ah, chan, ADC_GAIN_CAL)) { + INIT_CAL(&ahp->ah_adcGainCalData); + INSERT_CAL(ahp, &ahp->ah_adcGainCalData); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: enabling ADC Gain Calibration.\n", + __func__); + } + if (ath9k_hw_iscal_supported(ah, chan, ADC_DC_CAL)) { + INIT_CAL(&ahp->ah_adcDcCalData); + INSERT_CAL(ahp, &ahp->ah_adcDcCalData); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: enabling ADC DC Calibration.\n", + __func__); + } + if (ath9k_hw_iscal_supported(ah, chan, IQ_MISMATCH_CAL)) { + INIT_CAL(&ahp->ah_iqCalData); + INSERT_CAL(ahp, &ahp->ah_iqCalData); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: enabling IQ Calibration.\n", + __func__); + } + + ahp->ah_cal_list_curr = ahp->ah_cal_list; + + if (ahp->ah_cal_list_curr) + ath9k_hw_reset_calibration(ah, + ahp->ah_cal_list_curr); + } + + ichan->CalValid = 0; + + return true; +} + + +bool ath9k_hw_reset(struct ath_hal *ah, enum ath9k_opmode opmode, + struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode, + u8 txchainmask, u8 rxchainmask, + enum ath9k_ht_extprotspacing extprotspacing, + bool bChannelChange, + int *status) +{ +#define FAIL(_code) do { ecode = _code; goto bad; } while (0) + u32 saveLedState; + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *curchan = ah->ah_curchan; + u32 saveDefAntenna; + u32 macStaId1; + int ecode; + int i, rx_chainmask; + + ahp->ah_extprotspacing = extprotspacing; + ahp->ah_txchainmask = txchainmask; + ahp->ah_rxchainmask = rxchainmask; + + if (AR_SREV_9280(ah)) { + ahp->ah_txchainmask &= 0x3; + ahp->ah_rxchainmask &= 0x3; + } + + if (ath9k_hw_check_chan(ah, chan) == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channelFlags); + FAIL(-EINVAL); + } + + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + return false; + + if (curchan) + ath9k_hw_getnf(ah, curchan); + + if (bChannelChange && + (ahp->ah_chipFullSleep != true) && + (ah->ah_curchan != NULL) && + (chan->channel != ah->ah_curchan->channel) && + ((chan->channelFlags & CHANNEL_ALL) == + (ah->ah_curchan->channelFlags & CHANNEL_ALL)) && + (!AR_SREV_9280(ah) || (!IS_CHAN_A_5MHZ_SPACED(chan) && + !IS_CHAN_A_5MHZ_SPACED(ah-> + ah_curchan)))) { + + if (ath9k_hw_channel_change(ah, chan, macmode)) { + ath9k_hw_loadnf(ah, ah->ah_curchan); + ath9k_hw_start_nfcal(ah); + return true; + } + } + + saveDefAntenna = REG_READ(ah, AR_DEF_ANTENNA); + if (saveDefAntenna == 0) + saveDefAntenna = 1; + + macStaId1 = REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; + + saveLedState = REG_READ(ah, AR_CFG_LED) & + (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | + AR_CFG_LED_BLINK_THRESH_SEL | AR_CFG_LED_BLINK_SLOW); + + ath9k_hw_mark_phy_inactive(ah); + + if (!ath9k_hw_chip_reset(ah, chan)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: chip reset failed\n", + __func__); + FAIL(-EIO); + } + + if (AR_SREV_9280(ah)) { + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + AR_GPIO_JTAG_DISABLE); + + if (ah->ah_caps.halWirelessModes & ATH9K_MODE_SEL_11A) { + if (IS_CHAN_5GHZ(chan)) + ath9k_hw_set_gpio(ah, 9, 0); + else + ath9k_hw_set_gpio(ah, 9, 1); + } + ath9k_hw_cfg_output(ah, 9, ATH9K_GPIO_OUTPUT_MUX_AS_OUTPUT); + } + + ecode = ath9k_hw_process_ini(ah, chan, macmode); + if (ecode != 0) + goto bad; + + if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) + ath9k_hw_set_delta_slope(ah, chan); + + if (AR_SREV_9280_10_OR_LATER(ah)) + ath9k_hw_9280_spur_mitigate(ah, chan); + else + ath9k_hw_spur_mitigate(ah, chan); + + if (!ath9k_hw_eeprom_set_board_values(ah, chan)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: error setting board options\n", __func__); + FAIL(-EIO); + } + + ath9k_hw_decrease_chain_power(ah, chan); + + REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(ahp->ah_macaddr)); + REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(ahp->ah_macaddr + 4) + | macStaId1 + | AR_STA_ID1_RTS_USE_DEF + | (ah->ah_config. + ath_hal_6mb_ack ? AR_STA_ID1_ACKCTS_6MB : 0) + | ahp->ah_staId1Defaults); + ath9k_hw_set_operating_mode(ah, opmode); + + REG_WRITE(ah, AR_BSSMSKL, get_unaligned_le32(ahp->ah_bssidmask)); + REG_WRITE(ah, AR_BSSMSKU, get_unaligned_le16(ahp->ah_bssidmask + 4)); + + REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); + + REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(ahp->ah_bssid)); + REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(ahp->ah_bssid + 4) | + ((ahp->ah_assocId & 0x3fff) << AR_BSS_ID1_AID_S)); + + REG_WRITE(ah, AR_ISR, ~0); + + REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR); + + if (AR_SREV_9280_10_OR_LATER(ah)) { + if (!(ath9k_hw_ar9280_set_channel(ah, chan))) + FAIL(-EIO); + } else { + if (!(ath9k_hw_set_channel(ah, chan))) + FAIL(-EIO); + } + + for (i = 0; i < AR_NUM_DCU; i++) + REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); + + ahp->ah_intrTxqs = 0; + for (i = 0; i < ah->ah_caps.halTotalQueues; i++) + ath9k_hw_resettxqueue(ah, i); + + ath9k_hw_init_interrupt_masks(ah, opmode); + ath9k_hw_init_qos(ah); + + ath9k_hw_init_user_settings(ah); + + ah->ah_opmode = opmode; + + REG_WRITE(ah, AR_STA_ID1, + REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PRESERVE_SEQNUM); + + ath9k_hw_set_dma(ah); + + REG_WRITE(ah, AR_OBS, 8); + + if (ahp->ah_intrMitigation) { + + REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500); + REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 2000); + } + + ath9k_hw_init_bb(ah, chan); + + if (!ath9k_hw_init_cal(ah, chan)) + FAIL(-ENODEV); + + rx_chainmask = ahp->ah_rxchainmask; + if ((rx_chainmask == 0x5) || (rx_chainmask == 0x3)) { + REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); + REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); + } + + REG_WRITE(ah, AR_CFG_LED, saveLedState | AR_CFG_SCLK_32KHZ); + + if (AR_SREV_9100(ah)) { + u32 mask; + mask = REG_READ(ah, AR_CFG); + if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s CFG Byte Swap Set 0x%x\n", __func__, + mask); + } else { + mask = + INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB; + REG_WRITE(ah, AR_CFG, mask); + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s Setting CFG 0x%x\n", __func__, + REG_READ(ah, AR_CFG)); + } + } else { +#ifdef __BIG_ENDIAN + REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD); +#endif + } + + return true; +bad: + if (status) + *status = ecode; + return false; +#undef FAIL +} + +bool ath9k_hw_phy_disable(struct ath_hal *ah) +{ + return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM); +} + +bool ath9k_hw_disable(struct ath_hal *ah) +{ + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + return false; + + return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD); +} + +bool +ath9k_hw_calibrate(struct ath_hal *ah, struct ath9k_channel *chan, + u8 rxchainmask, bool longcal, + bool *isCalDone) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_cal_list *currCal = ahp->ah_cal_list_curr; + struct ath9k_channel *ichan = + ath9k_regd_check_channel(ah, chan); + + *isCalDone = true; + + if (ichan == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channelFlags); + return false; + } + + if (currCal && + (currCal->calState == CAL_RUNNING || + currCal->calState == CAL_WAITING)) { + ath9k_hw_per_calibration(ah, ichan, rxchainmask, currCal, + isCalDone); + if (*isCalDone) { + ahp->ah_cal_list_curr = currCal = currCal->calNext; + + if (currCal->calState == CAL_WAITING) { + *isCalDone = false; + ath9k_hw_reset_calibration(ah, currCal); + } + } + } + + if (longcal) { + ath9k_hw_getnf(ah, ichan); + ath9k_hw_loadnf(ah, ah->ah_curchan); + ath9k_hw_start_nfcal(ah); + + if ((ichan->channelFlags & CHANNEL_CW_INT) != 0) { + + chan->channelFlags |= CHANNEL_CW_INT; + ichan->channelFlags &= ~CHANNEL_CW_INT; + } + } + + return true; +} + +static void ath9k_hw_iqcal_collect(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ahp->ah_totalPowerMeasI[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); + ahp->ah_totalPowerMeasQ[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); + ahp->ah_totalIqCorrMeas[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", + ahp->ah_CalSamples, i, ahp->ah_totalPowerMeasI[i], + ahp->ah_totalPowerMeasQ[i], + ahp->ah_totalIqCorrMeas[i]); + } +} + +static void ath9k_hw_adc_gaincal_collect(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ahp->ah_totalAdcIOddPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); + ahp->ah_totalAdcIEvenPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); + ahp->ah_totalAdcQOddPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); + ahp->ah_totalAdcQEvenPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_3(i)); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%d: Chn %d oddi=0x%08x; eveni=0x%08x; " + "oddq=0x%08x; evenq=0x%08x;\n", + ahp->ah_CalSamples, i, + ahp->ah_totalAdcIOddPhase[i], + ahp->ah_totalAdcIEvenPhase[i], + ahp->ah_totalAdcQOddPhase[i], + ahp->ah_totalAdcQEvenPhase[i]); + } +} + +static void ath9k_hw_adc_dccal_collect(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ahp->ah_totalAdcDcOffsetIOddPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); + ahp->ah_totalAdcDcOffsetIEvenPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); + ahp->ah_totalAdcDcOffsetQOddPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); + ahp->ah_totalAdcDcOffsetQEvenPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_3(i)); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%d: Chn %d oddi=0x%08x; eveni=0x%08x; " + "oddq=0x%08x; evenq=0x%08x;\n", + ahp->ah_CalSamples, i, + ahp->ah_totalAdcDcOffsetIOddPhase[i], + ahp->ah_totalAdcDcOffsetIEvenPhase[i], + ahp->ah_totalAdcDcOffsetQOddPhase[i], + ahp->ah_totalAdcDcOffsetQEvenPhase[i]); + } +} + +static void ath9k_hw_iqcalibrate(struct ath_hal *ah, u8 numChains) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 powerMeasQ, powerMeasI, iqCorrMeas; + u32 qCoffDenom, iCoffDenom; + int32_t qCoff, iCoff; + int iqCorrNeg, i; + + for (i = 0; i < numChains; i++) { + powerMeasI = ahp->ah_totalPowerMeasI[i]; + powerMeasQ = ahp->ah_totalPowerMeasQ[i]; + iqCorrMeas = ahp->ah_totalIqCorrMeas[i]; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Starting IQ Cal and Correction for Chain %d\n", + i); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Orignal: Chn %diq_corr_meas = 0x%08x\n", + i, ahp->ah_totalIqCorrMeas[i]); + + iqCorrNeg = 0; + + + if (iqCorrMeas > 0x80000000) { + iqCorrMeas = (0xffffffff - iqCorrMeas) + 1; + iqCorrNeg = 1; + } + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n", + iqCorrNeg); + + iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128; + qCoffDenom = powerMeasQ / 64; + + if (powerMeasQ != 0) { + + iCoff = iqCorrMeas / iCoffDenom; + qCoff = powerMeasI / qCoffDenom - 64; + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d iCoff = 0x%08x\n", i, iCoff); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d qCoff = 0x%08x\n", i, qCoff); + + + iCoff = iCoff & 0x3f; + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "New: Chn %d iCoff = 0x%08x\n", i, iCoff); + if (iqCorrNeg == 0x0) + iCoff = 0x40 - iCoff; + + if (qCoff > 15) + qCoff = 15; + else if (qCoff <= -16) + qCoff = 16; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d : iCoff = 0x%x qCoff = 0x%x\n", + i, iCoff, qCoff); + + REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i), + AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, + iCoff); + REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i), + AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, + qCoff); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "IQ Cal and Correction done for Chain %d\n", + i); + } + } + + REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0), + AR_PHY_TIMING_CTRL4_IQCORR_ENABLE); +} + +static void +ath9k_hw_adc_gaincal_calibrate(struct ath_hal *ah, u8 numChains) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 iOddMeasOffset, iEvenMeasOffset, qOddMeasOffset, + qEvenMeasOffset; + u32 qGainMismatch, iGainMismatch, val, i; + + for (i = 0; i < numChains; i++) { + iOddMeasOffset = ahp->ah_totalAdcIOddPhase[i]; + iEvenMeasOffset = ahp->ah_totalAdcIEvenPhase[i]; + qOddMeasOffset = ahp->ah_totalAdcQOddPhase[i]; + qEvenMeasOffset = ahp->ah_totalAdcQEvenPhase[i]; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Starting ADC Gain Cal for Chain %d\n", i); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_i = 0x%08x\n", i, + iOddMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_i = 0x%08x\n", i, + iEvenMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_q = 0x%08x\n", i, + qOddMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_q = 0x%08x\n", i, + qEvenMeasOffset); + + if (iOddMeasOffset != 0 && qEvenMeasOffset != 0) { + iGainMismatch = + ((iEvenMeasOffset * 32) / + iOddMeasOffset) & 0x3f; + qGainMismatch = + ((qOddMeasOffset * 32) / + qEvenMeasOffset) & 0x3f; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d gain_mismatch_i = 0x%08x\n", i, + iGainMismatch); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d gain_mismatch_q = 0x%08x\n", i, + qGainMismatch); + + val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i)); + val &= 0xfffff000; + val |= (qGainMismatch) | (iGainMismatch << 6); + REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "ADC Gain Cal done for Chain %d\n", i); + } + } + + REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0), + REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) | + AR_PHY_NEW_ADC_GAIN_CORR_ENABLE); +} + +static void +ath9k_hw_adc_dccal_calibrate(struct ath_hal *ah, u8 numChains) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 iOddMeasOffset, iEvenMeasOffset, val, i; + int32_t qOddMeasOffset, qEvenMeasOffset, qDcMismatch, iDcMismatch; + const struct hal_percal_data *calData = + ahp->ah_cal_list_curr->calData; + u32 numSamples = + (1 << (calData->calCountMax + 5)) * calData->calNumSamples; + + for (i = 0; i < numChains; i++) { + iOddMeasOffset = ahp->ah_totalAdcDcOffsetIOddPhase[i]; + iEvenMeasOffset = ahp->ah_totalAdcDcOffsetIEvenPhase[i]; + qOddMeasOffset = ahp->ah_totalAdcDcOffsetQOddPhase[i]; + qEvenMeasOffset = ahp->ah_totalAdcDcOffsetQEvenPhase[i]; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Starting ADC DC Offset Cal for Chain %d\n", i); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_i = %d\n", i, + iOddMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_i = %d\n", i, + iEvenMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_q = %d\n", i, + qOddMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_q = %d\n", i, + qEvenMeasOffset); + + iDcMismatch = (((iEvenMeasOffset - iOddMeasOffset) * 2) / + numSamples) & 0x1ff; + qDcMismatch = (((qOddMeasOffset - qEvenMeasOffset) * 2) / + numSamples) & 0x1ff; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d dc_offset_mismatch_i = 0x%08x\n", i, + iDcMismatch); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d dc_offset_mismatch_q = 0x%08x\n", i, + qDcMismatch); + + val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i)); + val &= 0xc0000fff; + val |= (qDcMismatch << 12) | (iDcMismatch << 21); + REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "ADC DC Offset Cal done for Chain %d\n", i); + } + + REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0), + REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) | + AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE); +} + +bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *chan = ah->ah_curchan; + + ah->ah_powerLimit = min(limit, (u32) MAX_RATE_POWER); + + if (ath9k_hw_set_txpower(ah, &ahp->ah_eeprom, chan, + ath9k_regd_get_ctl(ah, chan), + ath9k_regd_get_antenna_allowed(ah, + chan), + chan->maxRegTxPower * 2, + min((u32) MAX_RATE_POWER, + (u32) ah->ah_powerLimit)) != 0) + return false; + + return true; +} + +void +ath9k_hw_get_channel_centers(struct ath_hal *ah, + struct ath9k_channel *chan, + struct chan_centers *centers) +{ + int8_t extoff; + struct ath_hal_5416 *ahp = AH5416(ah); + + if (!IS_CHAN_HT40(chan)) { + centers->ctl_center = centers->ext_center = + centers->synth_center = chan->channel; + return; + } + + if ((chan->chanmode == CHANNEL_A_HT40PLUS) || + (chan->chanmode == CHANNEL_G_HT40PLUS)) { + centers->synth_center = + chan->channel + HT40_CHANNEL_CENTER_SHIFT; + extoff = 1; + } else { + centers->synth_center = + chan->channel - HT40_CHANNEL_CENTER_SHIFT; + extoff = -1; + } + + centers->ctl_center = centers->synth_center - (extoff * + HT40_CHANNEL_CENTER_SHIFT); + centers->ext_center = centers->synth_center + (extoff * + ((ahp-> + ah_extprotspacing + == + ATH9K_HT_EXTPROTSPACING_20) + ? + HT40_CHANNEL_CENTER_SHIFT + : 15)); + +} + +void +ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan, + bool *isCalDone) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *ichan = + ath9k_regd_check_channel(ah, chan); + struct hal_cal_list *currCal = ahp->ah_cal_list_curr; + + *isCalDone = true; + + if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah)) + return; + + if (currCal == NULL) + return; + + if (ichan == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channelFlags); + return; + } + + + if (currCal->calState != CAL_DONE) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: Calibration state incorrect, %d\n", + __func__, currCal->calState); + return; + } + + + if (!ath9k_hw_iscal_supported(ah, chan, currCal->calData->calType)) + return; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: Resetting Cal %d state for channel %u/0x%x\n", + __func__, currCal->calData->calType, chan->channel, + chan->channelFlags); + + ichan->CalValid &= ~currCal->calData->calType; + currCal->calState = CAL_WAITING; + + *isCalDone = false; +} + +void ath9k_hw_getmac(struct ath_hal *ah, u8 *mac) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + memcpy(mac, ahp->ah_macaddr, ETH_ALEN); +} + +bool ath9k_hw_setmac(struct ath_hal *ah, const u8 *mac) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + memcpy(ahp->ah_macaddr, mac, ETH_ALEN); + return true; +} + +void ath9k_hw_getbssidmask(struct ath_hal *ah, u8 *mask) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + memcpy(mask, ahp->ah_bssidmask, ETH_ALEN); +} + +bool +ath9k_hw_setbssidmask(struct ath_hal *ah, const u8 *mask) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + memcpy(ahp->ah_bssidmask, mask, ETH_ALEN); + + REG_WRITE(ah, AR_BSSMSKL, get_unaligned_le32(ahp->ah_bssidmask)); + REG_WRITE(ah, AR_BSSMSKU, get_unaligned_le16(ahp->ah_bssidmask + 4)); + + return true; +} + +#ifdef CONFIG_ATH9K_RFKILL +static void ath9k_enable_rfkill(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + AR_GPIO_INPUT_EN_VAL_RFSILENT_BB); + + REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2, + AR_GPIO_INPUT_MUX2_RFSILENT); + + ath9k_hw_cfg_gpio_input(ah, ahp->ah_gpioSelect); + REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); + + if (ahp->ah_gpioBit == ath9k_hw_gpio_get(ah, ahp->ah_gpioSelect)) { + + ath9k_hw_set_gpio_intr(ah, ahp->ah_gpioSelect, + !ahp->ah_gpioBit); + } else { + ath9k_hw_set_gpio_intr(ah, ahp->ah_gpioSelect, + ahp->ah_gpioBit); + } +} +#endif + +void +ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid, + u16 assocId) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + memcpy(ahp->ah_bssid, bssid, ETH_ALEN); + ahp->ah_assocId = assocId; + + REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(ahp->ah_bssid)); + REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(ahp->ah_bssid + 4) | + ((assocId & 0x3fff) << AR_BSS_ID1_AID_S)); +} + +u64 ath9k_hw_gettsf64(struct ath_hal *ah) +{ + u64 tsf; + + tsf = REG_READ(ah, AR_TSF_U32); + tsf = (tsf << 32) | REG_READ(ah, AR_TSF_L32); + return tsf; +} + +void ath9k_hw_reset_tsf(struct ath_hal *ah) +{ + int count; + + count = 0; + while (REG_READ(ah, AR_SLP32_MODE) & AR_SLP32_TSF_WRITE_STATUS) { + count++; + if (count > 10) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: AR_SLP32_TSF_WRITE_STATUS limit exceeded\n", + __func__); + break; + } + udelay(10); + } + REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE); +} + +u32 ath9k_hw_getdefantenna(struct ath_hal *ah) +{ + return REG_READ(ah, AR_DEF_ANTENNA) & 0x7; +} + +void ath9k_hw_setantenna(struct ath_hal *ah, u32 antenna) +{ + REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7)); +} + +bool +ath9k_hw_setantennaswitch(struct ath_hal *ah, + enum ath9k_ant_setting settings, + struct ath9k_channel *chan, + u8 *tx_chainmask, + u8 *rx_chainmask, + u8 *antenna_cfgd) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + static u8 tx_chainmask_cfg, rx_chainmask_cfg; + + if (AR_SREV_9280(ah)) { + if (!tx_chainmask_cfg) { + + tx_chainmask_cfg = *tx_chainmask; + rx_chainmask_cfg = *rx_chainmask; + } + + switch (settings) { + case ATH9K_ANT_FIXED_A: + *tx_chainmask = ATH9K_ANTENNA0_CHAINMASK; + *rx_chainmask = ATH9K_ANTENNA0_CHAINMASK; + *antenna_cfgd = true; + break; + case ATH9K_ANT_FIXED_B: + if (ah->ah_caps.halTxChainMask > + ATH9K_ANTENNA1_CHAINMASK) { + *tx_chainmask = ATH9K_ANTENNA1_CHAINMASK; + } + *rx_chainmask = ATH9K_ANTENNA1_CHAINMASK; + *antenna_cfgd = true; + break; + case ATH9K_ANT_VARIABLE: + *tx_chainmask = tx_chainmask_cfg; + *rx_chainmask = rx_chainmask_cfg; + *antenna_cfgd = true; + break; + default: + break; + } + } else { + ahp->ah_diversityControl = settings; + } + + return true; +} + +void ath9k_hw_setopmode(struct ath_hal *ah) +{ + ath9k_hw_set_operating_mode(ah, ah->ah_opmode); +} + +bool +ath9k_hw_getcapability(struct ath_hal *ah, enum hal_capability_type type, + u32 capability, u32 *result) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + const struct hal_capabilities *pCap = &ah->ah_caps; + + switch (type) { + case HAL_CAP_CIPHER: + switch (capability) { + case ATH9K_CIPHER_AES_CCM: + case ATH9K_CIPHER_AES_OCB: + case ATH9K_CIPHER_TKIP: + case ATH9K_CIPHER_WEP: + case ATH9K_CIPHER_MIC: + case ATH9K_CIPHER_CLR: + return true; + default: + return false; + } + case HAL_CAP_TKIP_MIC: + switch (capability) { + case 0: + return true; + case 1: + return (ahp->ah_staId1Defaults & + AR_STA_ID1_CRPT_MIC_ENABLE) ? true : + false; + } + case HAL_CAP_TKIP_SPLIT: + return (ahp->ah_miscMode & AR_PCU_MIC_NEW_LOC_ENA) ? + false : true; + case HAL_CAP_WME_TKIPMIC: + return 0; + case HAL_CAP_PHYCOUNTERS: + return ahp->ah_hasHwPhyCounters ? 0 : -ENXIO; + case HAL_CAP_DIVERSITY: + return (REG_READ(ah, AR_PHY_CCK_DETECT) & + AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV) ? + true : false; + case HAL_CAP_PHYDIAG: + return true; + case HAL_CAP_MCAST_KEYSRCH: + switch (capability) { + case 0: + return true; + case 1: + if (REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_ADHOC) { + return false; + } else { + return (ahp->ah_staId1Defaults & + AR_STA_ID1_MCAST_KSRCH) ? true : + false; + } + } + return false; + case HAL_CAP_TSF_ADJUST: + return (ahp->ah_miscMode & AR_PCU_TX_ADD_TSF) ? + true : false; + case HAL_CAP_RFSILENT: + if (capability == 3) + return false; + case HAL_CAP_ANT_CFG_2GHZ: + *result = pCap->halNumAntCfg2GHz; + return true; + case HAL_CAP_ANT_CFG_5GHZ: + *result = pCap->halNumAntCfg5GHz; + return true; + case HAL_CAP_TXPOW: + switch (capability) { + case 0: + return 0; + case 1: + *result = ah->ah_powerLimit; + return 0; + case 2: + *result = ah->ah_maxPowerLevel; + return 0; + case 3: + *result = ah->ah_tpScale; + return 0; + } + return false; + default: + return false; + } +} + +int +ath9k_hw_select_antconfig(struct ath_hal *ah, u32 cfg) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *chan = ah->ah_curchan; + const struct hal_capabilities *pCap = &ah->ah_caps; + u16 ant_config; + u32 halNumAntConfig; + + halNumAntConfig = + IS_CHAN_2GHZ(chan) ? pCap->halNumAntCfg2GHz : pCap-> + halNumAntCfg5GHz; + + if (cfg < halNumAntConfig) { + if (!ath9k_hw_get_eeprom_antenna_cfg(ahp, chan, + cfg, &ant_config)) { + REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config); + return 0; + } + } + + return -EINVAL; +} + +bool ath9k_hw_intrpend(struct ath_hal *ah) +{ + u32 host_isr; + + if (AR_SREV_9100(ah)) + return true; + + host_isr = REG_READ(ah, AR_INTR_ASYNC_CAUSE); + if ((host_isr & AR_INTR_MAC_IRQ) && (host_isr != AR_INTR_SPURIOUS)) + return true; + + host_isr = REG_READ(ah, AR_INTR_SYNC_CAUSE); + if ((host_isr & AR_INTR_SYNC_DEFAULT) + && (host_isr != AR_INTR_SPURIOUS)) + return true; + + return false; +} + +bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked) +{ + u32 isr = 0; + u32 mask2 = 0; + struct hal_capabilities *pCap = &ah->ah_caps; + u32 sync_cause = 0; + bool fatal_int = false; + + if (!AR_SREV_9100(ah)) { + if (REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) { + if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) + == AR_RTC_STATUS_ON) { + isr = REG_READ(ah, AR_ISR); + } + } + + sync_cause = + REG_READ(ah, + AR_INTR_SYNC_CAUSE) & AR_INTR_SYNC_DEFAULT; + + *masked = 0; + + if (!isr && !sync_cause) + return false; + } else { + *masked = 0; + isr = REG_READ(ah, AR_ISR); + } + + if (isr) { + struct ath_hal_5416 *ahp = AH5416(ah); + + if (isr & AR_ISR_BCNMISC) { + u32 isr2; + isr2 = REG_READ(ah, AR_ISR_S2); + if (isr2 & AR_ISR_S2_TIM) + mask2 |= ATH9K_INT_TIM; + if (isr2 & AR_ISR_S2_DTIM) + mask2 |= ATH9K_INT_DTIM; + if (isr2 & AR_ISR_S2_DTIMSYNC) + mask2 |= ATH9K_INT_DTIMSYNC; + if (isr2 & (AR_ISR_S2_CABEND)) + mask2 |= ATH9K_INT_CABEND; + if (isr2 & AR_ISR_S2_GTT) + mask2 |= ATH9K_INT_GTT; + if (isr2 & AR_ISR_S2_CST) + mask2 |= ATH9K_INT_CST; + } + + isr = REG_READ(ah, AR_ISR_RAC); + if (isr == 0xffffffff) { + *masked = 0; + return false; + } + + *masked = isr & ATH9K_INT_COMMON; + + if (ahp->ah_intrMitigation) { + + if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM)) + *masked |= ATH9K_INT_RX; + } + + if (isr & (AR_ISR_RXOK | AR_ISR_RXERR)) + *masked |= ATH9K_INT_RX; + if (isr & + (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR | + AR_ISR_TXEOL)) { + u32 s0_s, s1_s; + + *masked |= ATH9K_INT_TX; + + s0_s = REG_READ(ah, AR_ISR_S0_S); + ahp->ah_intrTxqs |= MS(s0_s, AR_ISR_S0_QCU_TXOK); + ahp->ah_intrTxqs |= MS(s0_s, AR_ISR_S0_QCU_TXDESC); + + s1_s = REG_READ(ah, AR_ISR_S1_S); + ahp->ah_intrTxqs |= MS(s1_s, AR_ISR_S1_QCU_TXERR); + ahp->ah_intrTxqs |= MS(s1_s, AR_ISR_S1_QCU_TXEOL); + } + + if (isr & AR_ISR_RXORN) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "%s: receive FIFO overrun interrupt\n", + __func__); + } + + if (!AR_SREV_9100(ah)) { + if (!pCap->halAutoSleepSupport) { + u32 isr5 = REG_READ(ah, AR_ISR_S5_S); + if (isr5 & AR_ISR_S5_TIM_TIMER) + *masked |= ATH9K_INT_TIM_TIMER; + } + } + + *masked |= mask2; + } + if (AR_SREV_9100(ah)) + return true; + if (sync_cause) { + fatal_int = + (sync_cause & + (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) + ? true : false; + + if (fatal_int) { + if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "%s: received PCI FATAL interrupt\n", + __func__); + } + if (sync_cause & AR_INTR_SYNC_HOST1_PERR) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "%s: received PCI PERR interrupt\n", + __func__); + } + } + if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "%s: AR_INTR_SYNC_RADM_CPL_TIMEOUT\n", + __func__); + REG_WRITE(ah, AR_RC, AR_RC_HOSTIF); + REG_WRITE(ah, AR_RC, 0); + *masked |= ATH9K_INT_FATAL; + } + if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "%s: AR_INTR_SYNC_LOCAL_TIMEOUT\n", + __func__); + } + + REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause); + (void) REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR); + } + return true; +} + +enum ath9k_int ath9k_hw_intrget(struct ath_hal *ah) +{ + return AH5416(ah)->ah_maskReg; +} + +enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, enum ath9k_int ints) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 omask = ahp->ah_maskReg; + u32 mask, mask2; + struct hal_capabilities *pCap = &ah->ah_caps; + + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: 0x%x => 0x%x\n", __func__, + omask, ints); + + if (omask & ATH9K_INT_GLOBAL) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: disable IER\n", + __func__); + REG_WRITE(ah, AR_IER, AR_IER_DISABLE); + (void) REG_READ(ah, AR_IER); + if (!AR_SREV_9100(ah)) { + REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0); + (void) REG_READ(ah, AR_INTR_ASYNC_ENABLE); + + REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0); + (void) REG_READ(ah, AR_INTR_SYNC_ENABLE); + } + } + + mask = ints & ATH9K_INT_COMMON; + mask2 = 0; + + if (ints & ATH9K_INT_TX) { + if (ahp->ah_txOkInterruptMask) + mask |= AR_IMR_TXOK; + if (ahp->ah_txDescInterruptMask) + mask |= AR_IMR_TXDESC; + if (ahp->ah_txErrInterruptMask) + mask |= AR_IMR_TXERR; + if (ahp->ah_txEolInterruptMask) + mask |= AR_IMR_TXEOL; + } + if (ints & ATH9K_INT_RX) { + mask |= AR_IMR_RXERR; + if (ahp->ah_intrMitigation) + mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM; + else + mask |= AR_IMR_RXOK | AR_IMR_RXDESC; + if (!pCap->halAutoSleepSupport) + mask |= AR_IMR_GENTMR; + } + + if (ints & (ATH9K_INT_BMISC)) { + mask |= AR_IMR_BCNMISC; + if (ints & ATH9K_INT_TIM) + mask2 |= AR_IMR_S2_TIM; + if (ints & ATH9K_INT_DTIM) + mask2 |= AR_IMR_S2_DTIM; + if (ints & ATH9K_INT_DTIMSYNC) + mask2 |= AR_IMR_S2_DTIMSYNC; + if (ints & ATH9K_INT_CABEND) + mask2 |= (AR_IMR_S2_CABEND); + } + + if (ints & (ATH9K_INT_GTT | ATH9K_INT_CST)) { + mask |= AR_IMR_BCNMISC; + if (ints & ATH9K_INT_GTT) + mask2 |= AR_IMR_S2_GTT; + if (ints & ATH9K_INT_CST) + mask2 |= AR_IMR_S2_CST; + } + + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: new IMR 0x%x\n", __func__, + mask); + REG_WRITE(ah, AR_IMR, mask); + mask = REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM | + AR_IMR_S2_DTIM | + AR_IMR_S2_DTIMSYNC | + AR_IMR_S2_CABEND | + AR_IMR_S2_CABTO | + AR_IMR_S2_TSFOOR | + AR_IMR_S2_GTT | AR_IMR_S2_CST); + REG_WRITE(ah, AR_IMR_S2, mask | mask2); + ahp->ah_maskReg = ints; + + if (!pCap->halAutoSleepSupport) { + if (ints & ATH9K_INT_TIM_TIMER) + REG_SET_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); + else + REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); + } + + if (ints & ATH9K_INT_GLOBAL) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: enable IER\n", + __func__); + REG_WRITE(ah, AR_IER, AR_IER_ENABLE); + if (!AR_SREV_9100(ah)) { + REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, + AR_INTR_MAC_IRQ); + REG_WRITE(ah, AR_INTR_ASYNC_MASK, AR_INTR_MAC_IRQ); + + + REG_WRITE(ah, AR_INTR_SYNC_ENABLE, + AR_INTR_SYNC_DEFAULT); + REG_WRITE(ah, AR_INTR_SYNC_MASK, + AR_INTR_SYNC_DEFAULT); + } + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n", + REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER)); + } + + return omask; +} + +void +ath9k_hw_beaconinit(struct ath_hal *ah, + u32 next_beacon, u32 beacon_period) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int flags = 0; + + ahp->ah_beaconInterval = beacon_period; + + switch (ah->ah_opmode) { + case ATH9K_M_STA: + case ATH9K_M_MONITOR: + REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(next_beacon)); + REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, 0xffff); + REG_WRITE(ah, AR_NEXT_SWBA, 0x7ffff); + flags |= AR_TBTT_TIMER_EN; + break; + case ATH9K_M_IBSS: + REG_SET_BIT(ah, AR_TXCFG, + AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); + REG_WRITE(ah, AR_NEXT_NDP_TIMER, + TU_TO_USEC(next_beacon + + (ahp->ah_atimWindow ? ahp-> + ah_atimWindow : 1))); + flags |= AR_NDP_TIMER_EN; + case ATH9K_M_HOSTAP: + REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(next_beacon)); + REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, + TU_TO_USEC(next_beacon - + ah->ah_config. + ath_hal_dma_beacon_response_time)); + REG_WRITE(ah, AR_NEXT_SWBA, + TU_TO_USEC(next_beacon - + ah->ah_config. + ath_hal_sw_beacon_response_time)); + flags |= + AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN; + break; + } + + REG_WRITE(ah, AR_BEACON_PERIOD, TU_TO_USEC(beacon_period)); + REG_WRITE(ah, AR_DMA_BEACON_PERIOD, TU_TO_USEC(beacon_period)); + REG_WRITE(ah, AR_SWBA_PERIOD, TU_TO_USEC(beacon_period)); + REG_WRITE(ah, AR_NDP_PERIOD, TU_TO_USEC(beacon_period)); + + beacon_period &= ~ATH9K_BEACON_ENA; + if (beacon_period & ATH9K_BEACON_RESET_TSF) { + beacon_period &= ~ATH9K_BEACON_RESET_TSF; + ath9k_hw_reset_tsf(ah); + } + + REG_SET_BIT(ah, AR_TIMER_MODE, flags); +} + +void +ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah, + const struct ath9k_beacon_state *bs) +{ + u32 nextTbtt, beaconintval, dtimperiod, beacontimeout; + struct hal_capabilities *pCap = &ah->ah_caps; + + REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt)); + + REG_WRITE(ah, AR_BEACON_PERIOD, + TU_TO_USEC(bs->bs_intval & ATH9K_BEACON_PERIOD)); + REG_WRITE(ah, AR_DMA_BEACON_PERIOD, + TU_TO_USEC(bs->bs_intval & ATH9K_BEACON_PERIOD)); + + REG_RMW_FIELD(ah, AR_RSSI_THR, + AR_RSSI_THR_BM_THR, bs->bs_bmissthreshold); + + beaconintval = bs->bs_intval & ATH9K_BEACON_PERIOD; + + if (bs->bs_sleepduration > beaconintval) + beaconintval = bs->bs_sleepduration; + + dtimperiod = bs->bs_dtimperiod; + if (bs->bs_sleepduration > dtimperiod) + dtimperiod = bs->bs_sleepduration; + + if (beaconintval == dtimperiod) + nextTbtt = bs->bs_nextdtim; + else + nextTbtt = bs->bs_nexttbtt; + + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: next DTIM %d\n", __func__, + bs->bs_nextdtim); + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: next beacon %d\n", __func__, + nextTbtt); + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: beacon period %d\n", __func__, + beaconintval); + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: DTIM period %d\n", __func__, + dtimperiod); + + REG_WRITE(ah, AR_NEXT_DTIM, + TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP)); + REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(nextTbtt - SLEEP_SLOP)); + + REG_WRITE(ah, AR_SLEEP1, + SM((CAB_TIMEOUT_VAL << 3), AR_SLEEP1_CAB_TIMEOUT) + | AR_SLEEP1_ASSUME_DTIM); + + if (pCap->halAutoSleepSupport) + beacontimeout = (BEACON_TIMEOUT_VAL << 3); + else + beacontimeout = MIN_BEACON_TIMEOUT_VAL; + + REG_WRITE(ah, AR_SLEEP2, + SM(beacontimeout, AR_SLEEP2_BEACON_TIMEOUT)); + + REG_WRITE(ah, AR_TIM_PERIOD, TU_TO_USEC(beaconintval)); + REG_WRITE(ah, AR_DTIM_PERIOD, TU_TO_USEC(dtimperiod)); + + REG_SET_BIT(ah, AR_TIMER_MODE, + AR_TBTT_TIMER_EN | AR_TIM_TIMER_EN | + AR_DTIM_TIMER_EN); + +} + +bool ath9k_hw_keyisvalid(struct ath_hal *ah, u16 entry) +{ + if (entry < ah->ah_caps.halKeyCacheSize) { + u32 val = REG_READ(ah, AR_KEYTABLE_MAC1(entry)); + if (val & AR_KEYTABLE_VALID) + return true; + } + return false; +} + +bool ath9k_hw_keyreset(struct ath_hal *ah, u16 entry) +{ + u32 keyType; + + if (entry >= ah->ah_caps.halKeyCacheSize) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: entry %u out of range\n", __func__, entry); + return false; + } + keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry)); + + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR); + REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0); + + if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) { + u16 micentry = entry + 64; + + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); + + } + + if (ah->ah_curchan == NULL) + return true; + + return true; +} + +bool +ath9k_hw_keysetmac(struct ath_hal *ah, u16 entry, + const u8 *mac) +{ + u32 macHi, macLo; + + if (entry >= ah->ah_caps.halKeyCacheSize) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: entry %u out of range\n", __func__, entry); + return false; + } + + if (mac != NULL) { + macHi = (mac[5] << 8) | mac[4]; + macLo = (mac[3] << 24) | (mac[2] << 16) + | (mac[1] << 8) | mac[0]; + macLo >>= 1; + macLo |= (macHi & 1) << 31; + macHi >>= 1; + } else { + macLo = macHi = 0; + } + REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo); + REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | AR_KEYTABLE_VALID); + + return true; +} + +bool +ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry, + const struct ath9k_keyval *k, + const u8 *mac, int xorKey) +{ + const struct hal_capabilities *pCap = &ah->ah_caps; + u32 key0, key1, key2, key3, key4; + u32 keyType; + u32 xorMask = xorKey ? + (ATH9K_KEY_XOR << 24 | ATH9K_KEY_XOR << 16 | ATH9K_KEY_XOR << 8 + | ATH9K_KEY_XOR) : 0; + struct ath_hal_5416 *ahp = AH5416(ah); + + if (entry >= pCap->halKeyCacheSize) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: entry %u out of range\n", __func__, entry); + return false; + } + switch (k->kv_type) { + case ATH9K_CIPHER_AES_OCB: + keyType = AR_KEYTABLE_TYPE_AES; + break; + case ATH9K_CIPHER_AES_CCM: + if (!pCap->halCipherAesCcmSupport) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: AES-CCM not supported by " + "mac rev 0x%x\n", __func__, + ah->ah_macRev); + return false; + } + keyType = AR_KEYTABLE_TYPE_CCM; + break; + case ATH9K_CIPHER_TKIP: + keyType = AR_KEYTABLE_TYPE_TKIP; + if (ATH9K_IS_MIC_ENABLED(ah) + && entry + 64 >= pCap->halKeyCacheSize) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: entry %u inappropriate for TKIP\n", + __func__, entry); + return false; + } + break; + case ATH9K_CIPHER_WEP: + if (k->kv_len < 40 / NBBY) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: WEP key length %u too small\n", + __func__, k->kv_len); + return false; + } + if (k->kv_len <= 40 / NBBY) + keyType = AR_KEYTABLE_TYPE_40; + else if (k->kv_len <= 104 / NBBY) + keyType = AR_KEYTABLE_TYPE_104; + else + keyType = AR_KEYTABLE_TYPE_128; + break; + case ATH9K_CIPHER_CLR: + keyType = AR_KEYTABLE_TYPE_CLR; + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: cipher %u not supported\n", __func__, + k->kv_type); + return false; + } + + key0 = get_unaligned_le32(k->kv_val + 0) ^ xorMask; + key1 = (get_unaligned_le16(k->kv_val + 4) ^ xorMask) & 0xffff; + key2 = get_unaligned_le32(k->kv_val + 6) ^ xorMask; + key3 = (get_unaligned_le16(k->kv_val + 10) ^ xorMask) & 0xffff; + key4 = get_unaligned_le32(k->kv_val + 12) ^ xorMask; + if (k->kv_len <= 104 / NBBY) + key4 &= 0xff; + + if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) { + u16 micentry = entry + 64; + + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1); + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); + (void) ath9k_hw_keysetmac(ah, entry, mac); + + if (ahp->ah_miscMode & AR_PCU_MIC_NEW_LOC_ENA) { + u32 mic0, mic1, mic2, mic3, mic4; + + mic0 = get_unaligned_le32(k->kv_mic + 0); + mic2 = get_unaligned_le32(k->kv_mic + 4); + mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff; + mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff; + mic4 = get_unaligned_le32(k->kv_txmic + 4); + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1); + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3); + REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), + AR_KEYTABLE_TYPE_CLR); + + } else { + u32 mic0, mic2; + + mic0 = get_unaligned_le32(k->kv_mic + 0); + mic2 = get_unaligned_le32(k->kv_mic + 4); + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), + AR_KEYTABLE_TYPE_CLR); + } + REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); + } else { + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); + + (void) ath9k_hw_keysetmac(ah, entry, mac); + } + + if (ah->ah_curchan == NULL) + return true; + + return true; +} + +bool +ath9k_hw_updatetxtriglevel(struct ath_hal *ah, bool bIncTrigLevel) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 txcfg, curLevel, newLevel; + enum ath9k_int omask; + + if (ah->ah_txTrigLevel >= MAX_TX_FIFO_THRESHOLD) + return false; + + omask = ath9k_hw_set_interrupts(ah, + ahp->ah_maskReg & ~ATH9K_INT_GLOBAL); + + txcfg = REG_READ(ah, AR_TXCFG); + curLevel = MS(txcfg, AR_FTRIG); + newLevel = curLevel; + if (bIncTrigLevel) { + if (curLevel < MAX_TX_FIFO_THRESHOLD) + newLevel++; + } else if (curLevel > MIN_TX_FIFO_THRESHOLD) + newLevel--; + if (newLevel != curLevel) + REG_WRITE(ah, AR_TXCFG, + (txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG)); + + ath9k_hw_set_interrupts(ah, omask); + + ah->ah_txTrigLevel = newLevel; + + return newLevel != curLevel; +} + +static bool ath9k_hw_set_txq_props(struct ath_hal *ah, + struct ath9k_tx_queue_info *qi, + const struct ath9k_txq_info *qInfo) +{ + u32 cw; + + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue\n", + __func__); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: queue %p\n", __func__, qi); + + qi->tqi_ver = qInfo->tqi_ver; + qi->tqi_subtype = qInfo->tqi_subtype; + qi->tqi_qflags = qInfo->tqi_qflags; + qi->tqi_priority = qInfo->tqi_priority; + if (qInfo->tqi_aifs != ATH9K_TXQ_USEDEFAULT) + qi->tqi_aifs = min(qInfo->tqi_aifs, 255U); + else + qi->tqi_aifs = INIT_AIFS; + if (qInfo->tqi_cwmin != ATH9K_TXQ_USEDEFAULT) { + cw = min(qInfo->tqi_cwmin, 1024U); + qi->tqi_cwmin = 1; + while (qi->tqi_cwmin < cw) + qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1; + } else + qi->tqi_cwmin = qInfo->tqi_cwmin; + if (qInfo->tqi_cwmax != ATH9K_TXQ_USEDEFAULT) { + cw = min(qInfo->tqi_cwmax, 1024U); + qi->tqi_cwmax = 1; + while (qi->tqi_cwmax < cw) + qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1; + } else + qi->tqi_cwmax = INIT_CWMAX; + + if (qInfo->tqi_shretry != 0) + qi->tqi_shretry = min((u32) qInfo->tqi_shretry, 15U); + else + qi->tqi_shretry = INIT_SH_RETRY; + if (qInfo->tqi_lgretry != 0) + qi->tqi_lgretry = min((u32) qInfo->tqi_lgretry, 15U); + else + qi->tqi_lgretry = INIT_LG_RETRY; + qi->tqi_cbrPeriod = qInfo->tqi_cbrPeriod; + qi->tqi_cbrOverflowLimit = qInfo->tqi_cbrOverflowLimit; + qi->tqi_burstTime = qInfo->tqi_burstTime; + qi->tqi_readyTime = qInfo->tqi_readyTime; + + switch (qInfo->tqi_subtype) { + case ATH9K_WME_UPSD: + if (qi->tqi_type == ATH9K_TX_QUEUE_DATA) + qi->tqi_intFlags = ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS; + break; + default: + break; + } + return true; +} + +bool ath9k_hw_settxqueueprops(struct ath_hal *ah, int q, + const struct ath9k_txq_info *qInfo) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_capabilities *pCap = &ah->ah_caps; + + if (q >= pCap->halTotalQueues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", + __func__, q); + return false; + } + return ath9k_hw_set_txq_props(ah, &ahp->ah_txq[q], qInfo); +} + +static bool ath9k_hw_get_txq_props(struct ath_hal *ah, + struct ath9k_txq_info *qInfo, + const struct ath9k_tx_queue_info *qi) +{ + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue\n", + __func__); + return false; + } + + qInfo->tqi_qflags = qi->tqi_qflags; + qInfo->tqi_ver = qi->tqi_ver; + qInfo->tqi_subtype = qi->tqi_subtype; + qInfo->tqi_qflags = qi->tqi_qflags; + qInfo->tqi_priority = qi->tqi_priority; + qInfo->tqi_aifs = qi->tqi_aifs; + qInfo->tqi_cwmin = qi->tqi_cwmin; + qInfo->tqi_cwmax = qi->tqi_cwmax; + qInfo->tqi_shretry = qi->tqi_shretry; + qInfo->tqi_lgretry = qi->tqi_lgretry; + qInfo->tqi_cbrPeriod = qi->tqi_cbrPeriod; + qInfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit; + qInfo->tqi_burstTime = qi->tqi_burstTime; + qInfo->tqi_readyTime = qi->tqi_readyTime; + + return true; +} + +bool +ath9k_hw_gettxqueueprops(struct ath_hal *ah, int q, + struct ath9k_txq_info *qInfo) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_capabilities *pCap = &ah->ah_caps; + + if (q >= pCap->halTotalQueues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", + __func__, q); + return false; + } + return ath9k_hw_get_txq_props(ah, qInfo, &ahp->ah_txq[q]); +} + +int +ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type, + const struct ath9k_txq_info *qInfo) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_tx_queue_info *qi; + struct hal_capabilities *pCap = &ah->ah_caps; + int q; + + switch (type) { + case ATH9K_TX_QUEUE_BEACON: + q = pCap->halTotalQueues - 1; + break; + case ATH9K_TX_QUEUE_CAB: + q = pCap->halTotalQueues - 2; + break; + case ATH9K_TX_QUEUE_PSPOLL: + q = 1; + break; + case ATH9K_TX_QUEUE_UAPSD: + q = pCap->halTotalQueues - 3; + break; + case ATH9K_TX_QUEUE_DATA: + for (q = 0; q < pCap->halTotalQueues; q++) + if (ahp->ah_txq[q].tqi_type == + ATH9K_TX_QUEUE_INACTIVE) + break; + if (q == pCap->halTotalQueues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: no available tx queue\n", __func__); + return -1; + } + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: bad tx queue type %u\n", + __func__, type); + return -1; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: queue %u\n", __func__, q); + + qi = &ahp->ah_txq[q]; + if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: tx queue %u already active\n", __func__, q); + return -1; + } + memset(qi, 0, sizeof(struct ath9k_tx_queue_info)); + qi->tqi_type = type; + if (qInfo == NULL) { + qi->tqi_qflags = + TXQ_FLAG_TXOKINT_ENABLE + | TXQ_FLAG_TXERRINT_ENABLE + | TXQ_FLAG_TXDESCINT_ENABLE | TXQ_FLAG_TXURNINT_ENABLE; + qi->tqi_aifs = INIT_AIFS; + qi->tqi_cwmin = ATH9K_TXQ_USEDEFAULT; + 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) ath9k_hw_settxqueueprops(ah, q, qInfo); + } + + return q; +} + +static void +ath9k_hw_set_txq_interrupts(struct ath_hal *ah, + struct ath9k_tx_queue_info *qi) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "%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); + + REG_WRITE(ah, AR_IMR_S0, + SM(ahp->ah_txOkInterruptMask, AR_IMR_S0_QCU_TXOK) + | SM(ahp->ah_txDescInterruptMask, AR_IMR_S0_QCU_TXDESC)); + REG_WRITE(ah, AR_IMR_S1, + SM(ahp->ah_txErrInterruptMask, AR_IMR_S1_QCU_TXERR) + | SM(ahp->ah_txEolInterruptMask, AR_IMR_S1_QCU_TXEOL)); + REG_RMW_FIELD(ah, AR_IMR_S2, + AR_IMR_S2_QCU_TXURN, ahp->ah_txUrnInterruptMask); +} + +bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_tx_queue_info *qi; + + if (q >= pCap->halTotalQueues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", + __func__, q); + return false; + } + qi = &ahp->ah_txq[q]; + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue %u\n", + __func__, q); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: release queue %u\n", + __func__, q); + + qi->tqi_type = ATH9K_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); + ath9k_hw_set_txq_interrupts(ah, qi); + + return true; +} + +bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_channel *chan = ah->ah_curchan; + struct ath9k_tx_queue_info *qi; + u32 cwMin, chanCwMin, value; + + if (q >= pCap->halTotalQueues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", + __func__, q); + return false; + } + qi = &ahp->ah_txq[q]; + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue %u\n", + __func__, q); + return true; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: reset queue %u\n", __func__, q); + + if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) { + if (chan && IS_CHAN_B(chan)) + chanCwMin = INIT_CWMIN_11B; + else + chanCwMin = INIT_CWMIN; + + for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1); + } else + cwMin = qi->tqi_cwmin; + + 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)); + + 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_shretry, AR_D_RETRY_LIMIT_FR_SH) + ); + + REG_WRITE(ah, AR_QMISC(q), AR_Q_MISC_DCU_EARLY_TERM_REQ); + REG_WRITE(ah, AR_DMISC(q), + AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x2); + + if (qi->tqi_cbrPeriod) { + REG_WRITE(ah, AR_QCBRCFG(q), + SM(qi->tqi_cbrPeriod, AR_Q_CBRCFG_INTERVAL) + | SM(qi->tqi_cbrOverflowLimit, + AR_Q_CBRCFG_OVF_THRESH)); + REG_WRITE(ah, AR_QMISC(q), + REG_READ(ah, + AR_QMISC(q)) | AR_Q_MISC_FSP_CBR | (qi-> + tqi_cbrOverflowLimit + ? + AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN + : + 0)); + } + if (qi->tqi_readyTime && (qi->tqi_type != ATH9K_TX_QUEUE_CAB)) { + REG_WRITE(ah, AR_QRDYTIMECFG(q), + SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_DURATION) | + AR_Q_RDYTIMECFG_EN); + } + + 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_burstTime + && (qi->tqi_qflags & TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)) { + REG_WRITE(ah, AR_QMISC(q), + REG_READ(ah, + AR_QMISC(q)) | + AR_Q_MISC_RDYTIME_EXP_POLICY); + + } + + if (qi->tqi_qflags & TXQ_FLAG_BACKOFF_DISABLE) { + REG_WRITE(ah, AR_DMISC(q), + REG_READ(ah, AR_DMISC(q)) | + AR_D_MISC_POST_FR_BKOFF_DIS); + } + if (qi->tqi_qflags & TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) { + REG_WRITE(ah, AR_DMISC(q), + REG_READ(ah, AR_DMISC(q)) | + AR_D_MISC_FRAG_BKOFF_EN); + } + switch (qi->tqi_type) { + case ATH9K_TX_QUEUE_BEACON: + REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q)) + | AR_Q_MISC_FSP_DBA_GATED + | AR_Q_MISC_BEACON_USE + | AR_Q_MISC_CBR_INCR_DIS1); + + REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) + | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << + AR_D_MISC_ARB_LOCKOUT_CNTRL_S) + | AR_D_MISC_BEACON_USE + | AR_D_MISC_POST_FR_BKOFF_DIS); + break; + case ATH9K_TX_QUEUE_CAB: + REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q)) + | AR_Q_MISC_FSP_DBA_GATED + | AR_Q_MISC_CBR_INCR_DIS1 + | AR_Q_MISC_CBR_INCR_DIS0); + value = (qi->tqi_readyTime + - (ah->ah_config.ath_hal_sw_beacon_response_time - + ah->ah_config.ath_hal_dma_beacon_response_time) + - + ah->ah_config.ath_hal_additional_swba_backoff) * + 1024; + REG_WRITE(ah, AR_QRDYTIMECFG(q), + value | AR_Q_RDYTIMECFG_EN); + REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) + | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << + AR_D_MISC_ARB_LOCKOUT_CNTRL_S)); + break; + case ATH9K_TX_QUEUE_PSPOLL: + REG_WRITE(ah, AR_QMISC(q), + REG_READ(ah, + AR_QMISC(q)) | AR_Q_MISC_CBR_INCR_DIS1); + break; + case ATH9K_TX_QUEUE_UAPSD: + REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) + | AR_D_MISC_POST_FR_BKOFF_DIS); + break; + default: + break; + } + + if (qi->tqi_intFlags & ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS) { + REG_WRITE(ah, AR_DMISC(q), + REG_READ(ah, AR_DMISC(q)) | + SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, + AR_D_MISC_ARB_LOCKOUT_CNTRL) | + AR_D_MISC_POST_FR_BKOFF_DIS); + } + + if (qi->tqi_qflags & TXQ_FLAG_TXOKINT_ENABLE) + ahp->ah_txOkInterruptMask |= 1 << q; + else + ahp->ah_txOkInterruptMask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXERRINT_ENABLE) + ahp->ah_txErrInterruptMask |= 1 << q; + else + ahp->ah_txErrInterruptMask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXDESCINT_ENABLE) + ahp->ah_txDescInterruptMask |= 1 << q; + else + ahp->ah_txDescInterruptMask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXEOLINT_ENABLE) + ahp->ah_txEolInterruptMask |= 1 << q; + else + ahp->ah_txEolInterruptMask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXURNINT_ENABLE) + ahp->ah_txUrnInterruptMask |= 1 << q; + else + ahp->ah_txUrnInterruptMask &= ~(1 << q); + ath9k_hw_set_txq_interrupts(ah, qi); + + return true; +} + +void ath9k_hw_gettxintrtxqs(struct ath_hal *ah, u32 *txqs) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + *txqs &= ahp->ah_intrTxqs; + ahp->ah_intrTxqs &= ~(*txqs); +} + +bool +ath9k_hw_filltxdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 segLen, bool firstSeg, + bool lastSeg, const struct ath_desc *ds0) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + if (firstSeg) { + ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore); + } else if (lastSeg) { + ads->ds_ctl0 = 0; + ads->ds_ctl1 = segLen; + ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2; + ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3; + } else { + ads->ds_ctl0 = 0; + ads->ds_ctl1 = segLen | AR_TxMore; + ads->ds_ctl2 = 0; + ads->ds_ctl3 = 0; + } + ads->ds_txstatus0 = ads->ds_txstatus1 = 0; + ads->ds_txstatus2 = ads->ds_txstatus3 = 0; + ads->ds_txstatus4 = ads->ds_txstatus5 = 0; + ads->ds_txstatus6 = ads->ds_txstatus7 = 0; + ads->ds_txstatus8 = ads->ds_txstatus9 = 0; + return true; +} + +void ath9k_hw_cleartxdesc(struct ath_hal *ah, struct ath_desc *ds) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_txstatus0 = ads->ds_txstatus1 = 0; + ads->ds_txstatus2 = ads->ds_txstatus3 = 0; + ads->ds_txstatus4 = ads->ds_txstatus5 = 0; + ads->ds_txstatus6 = ads->ds_txstatus7 = 0; + ads->ds_txstatus8 = ads->ds_txstatus9 = 0; +} + +int +ath9k_hw_txprocdesc(struct ath_hal *ah, struct ath_desc *ds) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + if ((ads->ds_txstatus9 & AR_TxDone) == 0) + return -EINPROGRESS; + + ds->ds_txstat.ts_seqnum = MS(ads->ds_txstatus9, AR_SeqNum); + ds->ds_txstat.ts_tstamp = ads->AR_SendTimestamp; + ds->ds_txstat.ts_status = 0; + ds->ds_txstat.ts_flags = 0; + + if (ads->ds_txstatus1 & AR_ExcessiveRetries) + ds->ds_txstat.ts_status |= ATH9K_TXERR_XRETRY; + if (ads->ds_txstatus1 & AR_Filtered) + ds->ds_txstat.ts_status |= ATH9K_TXERR_FILT; + if (ads->ds_txstatus1 & AR_FIFOUnderrun) + ds->ds_txstat.ts_status |= ATH9K_TXERR_FIFO; + if (ads->ds_txstatus9 & AR_TxOpExceeded) + ds->ds_txstat.ts_status |= ATH9K_TXERR_XTXOP; + if (ads->ds_txstatus1 & AR_TxTimerExpired) + ds->ds_txstat.ts_status |= ATH9K_TXERR_TIMER_EXPIRED; + + if (ads->ds_txstatus1 & AR_DescCfgErr) + ds->ds_txstat.ts_flags |= ATH9K_TX_DESC_CFG_ERR; + if (ads->ds_txstatus1 & AR_TxDataUnderrun) { + ds->ds_txstat.ts_flags |= ATH9K_TX_DATA_UNDERRUN; + ath9k_hw_updatetxtriglevel(ah, true); + } + if (ads->ds_txstatus1 & AR_TxDelimUnderrun) { + ds->ds_txstat.ts_flags |= ATH9K_TX_DELIM_UNDERRUN; + ath9k_hw_updatetxtriglevel(ah, true); + } + if (ads->ds_txstatus0 & AR_TxBaStatus) { + ds->ds_txstat.ts_flags |= ATH9K_TX_BA; + ds->ds_txstat.ba_low = ads->AR_BaBitmapLow; + ds->ds_txstat.ba_high = ads->AR_BaBitmapHigh; + } + + ds->ds_txstat.ts_rateindex = MS(ads->ds_txstatus9, AR_FinalTxIdx); + switch (ds->ds_txstat.ts_rateindex) { + case 0: + ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate0); + break; + case 1: + ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate1); + break; + case 2: + ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate2); + break; + case 3: + ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate3); + break; + } + + ds->ds_txstat.ts_rssi = MS(ads->ds_txstatus5, AR_TxRSSICombined); + ds->ds_txstat.ts_rssi_ctl0 = MS(ads->ds_txstatus0, AR_TxRSSIAnt00); + ds->ds_txstat.ts_rssi_ctl1 = MS(ads->ds_txstatus0, AR_TxRSSIAnt01); + ds->ds_txstat.ts_rssi_ctl2 = MS(ads->ds_txstatus0, AR_TxRSSIAnt02); + ds->ds_txstat.ts_rssi_ext0 = MS(ads->ds_txstatus5, AR_TxRSSIAnt10); + ds->ds_txstat.ts_rssi_ext1 = MS(ads->ds_txstatus5, AR_TxRSSIAnt11); + ds->ds_txstat.ts_rssi_ext2 = MS(ads->ds_txstatus5, AR_TxRSSIAnt12); + ds->ds_txstat.evm0 = ads->AR_TxEVM0; + ds->ds_txstat.evm1 = ads->AR_TxEVM1; + ds->ds_txstat.evm2 = ads->AR_TxEVM2; + ds->ds_txstat.ts_shortretry = MS(ads->ds_txstatus1, AR_RTSFailCnt); + ds->ds_txstat.ts_longretry = MS(ads->ds_txstatus1, AR_DataFailCnt); + ds->ds_txstat.ts_virtcol = MS(ads->ds_txstatus1, AR_VirtRetryCnt); + ds->ds_txstat.ts_antenna = 1; + + return 0; +} + +void +ath9k_hw_set11n_txdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 pktLen, enum ath9k_pkt_type type, u32 txPower, + u32 keyIx, enum ath9k_key_type keyType, u32 flags) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + struct ath_hal_5416 *ahp = AH5416(ah); + + txPower += ahp->ah_txPowerIndexOffset; + if (txPower > 63) + txPower = 63; + + ads->ds_ctl0 = (pktLen & AR_FrameLen) + | (flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) + | SM(txPower, AR_XmitPower) + | (flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) + | (flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0) + | (flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0) + | (keyIx != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0); + + ads->ds_ctl1 = + (keyIx != ATH9K_TXKEYIX_INVALID ? SM(keyIx, AR_DestIdx) : 0) + | SM(type, AR_FrameType) + | (flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0) + | (flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0) + | (flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0); + + ads->ds_ctl6 = SM(keyType, AR_EncrType); + + if (AR_SREV_9285(ah)) { + + ads->ds_ctl8 = 0; + ads->ds_ctl9 = 0; + ads->ds_ctl10 = 0; + ads->ds_ctl11 = 0; + } +} + +void +ath9k_hw_set11n_ratescenario(struct ath_hal *ah, struct ath_desc *ds, + struct ath_desc *lastds, + u32 durUpdateEn, u32 rtsctsRate, + u32 rtsctsDuration, + struct ath9k_11n_rate_series series[], + u32 nseries, u32 flags) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + struct ar5416_desc *last_ads = AR5416DESC(lastds); + u32 ds_ctl0; + + (void) nseries; + (void) rtsctsDuration; + + if (flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA)) { + ds_ctl0 = ads->ds_ctl0; + + if (flags & ATH9K_TXDESC_RTSENA) { + ds_ctl0 &= ~AR_CTSEnable; + ds_ctl0 |= AR_RTSEnable; + } else { + ds_ctl0 &= ~AR_RTSEnable; + ds_ctl0 |= AR_CTSEnable; + } + + ads->ds_ctl0 = ds_ctl0; + } else { + ads->ds_ctl0 = + (ads->ds_ctl0 & ~(AR_RTSEnable | AR_CTSEnable)); + } + + ads->ds_ctl2 = set11nTries(series, 0) + | set11nTries(series, 1) + | set11nTries(series, 2) + | set11nTries(series, 3) + | (durUpdateEn ? AR_DurUpdateEna : 0) + | SM(0, AR_BurstDur); + + 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); + last_ads->ds_ctl2 = ads->ds_ctl2; + last_ads->ds_ctl3 = ads->ds_ctl3; +} + +void +ath9k_hw_set11n_aggr_first(struct ath_hal *ah, struct ath_desc *ds, + u32 aggrLen) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); + + ads->ds_ctl6 &= ~AR_AggrLen; + ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen); +} + +void +ath9k_hw_set11n_aggr_middle(struct ath_hal *ah, struct ath_desc *ds, + u32 numDelims) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + unsigned int ctl6; + + ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); + + ctl6 = ads->ds_ctl6; + ctl6 &= ~AR_PadDelim; + ctl6 |= SM(numDelims, AR_PadDelim); + ads->ds_ctl6 = ctl6; +} + +void ath9k_hw_set11n_aggr_last(struct ath_hal *ah, struct ath_desc *ds) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_ctl1 |= AR_IsAggr; + ads->ds_ctl1 &= ~AR_MoreAggr; + ads->ds_ctl6 &= ~AR_PadDelim; +} + +void ath9k_hw_clr11n_aggr(struct ath_hal *ah, struct ath_desc *ds) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr); +} + +void +ath9k_hw_set11n_burstduration(struct ath_hal *ah, struct ath_desc *ds, + u32 burstDuration) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_ctl2 &= ~AR_BurstDur; + ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur); +} + +void +ath9k_hw_set11n_virtualmorefrag(struct ath_hal *ah, struct ath_desc *ds, + u32 vmf) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + if (vmf) + ads->ds_ctl0 |= AR_VirtMoreFrag; + else + ads->ds_ctl0 &= ~AR_VirtMoreFrag; +} + +void ath9k_hw_putrxbuf(struct ath_hal *ah, u32 rxdp) +{ + REG_WRITE(ah, AR_RXDP, rxdp); +} + +void ath9k_hw_rxena(struct ath_hal *ah) +{ + REG_WRITE(ah, AR_CR, AR_CR_RXE); +} + +bool ath9k_hw_setrxabort(struct ath_hal *ah, bool set) +{ + if (set) { + + REG_SET_BIT(ah, AR_DIAG_SW, + (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + + if (!ath9k_hw_wait + (ah, AR_OBS_BUS_1, AR_OBS_BUS_1_RX_STATE, 0)) { + u32 reg; + + REG_CLR_BIT(ah, AR_DIAG_SW, + (AR_DIAG_RX_DIS | + AR_DIAG_RX_ABORT)); + + reg = REG_READ(ah, AR_OBS_BUS_1); + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "%s: rx failed to go idle in 10 ms RXSM=0x%x\n", + __func__, reg); + + return false; + } + } else { + REG_CLR_BIT(ah, AR_DIAG_SW, + (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + } + + return true; +} + +void +ath9k_hw_setmcastfilter(struct ath_hal *ah, u32 filter0, + u32 filter1) +{ + REG_WRITE(ah, AR_MCAST_FIL0, filter0); + REG_WRITE(ah, AR_MCAST_FIL1, filter1); +} + +bool +ath9k_hw_setuprxdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 size, u32 flags) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + struct hal_capabilities *pCap = &ah->ah_caps; + + ads->ds_ctl1 = size & AR_BufLen; + if (flags & ATH9K_RXDESC_INTREQ) + ads->ds_ctl1 |= AR_RxIntrReq; + + ads->ds_rxstatus8 &= ~AR_RxDone; + if (!pCap->halAutoSleepSupport) + memset(&(ads->u), 0, sizeof(ads->u)); + return true; +} + +int +ath9k_hw_rxprocdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 pa, struct ath_desc *nds, u64 tsf) +{ + struct ar5416_desc ads; + struct ar5416_desc *adsp = AR5416DESC(ds); + + if ((adsp->ds_rxstatus8 & AR_RxDone) == 0) + return -EINPROGRESS; + + ads.u.rx = adsp->u.rx; + + ds->ds_rxstat.rs_status = 0; + ds->ds_rxstat.rs_flags = 0; + + ds->ds_rxstat.rs_datalen = ads.ds_rxstatus1 & AR_DataLen; + ds->ds_rxstat.rs_tstamp = ads.AR_RcvTimestamp; + + ds->ds_rxstat.rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined); + ds->ds_rxstat.rs_rssi_ctl0 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt00); + ds->ds_rxstat.rs_rssi_ctl1 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt01); + ds->ds_rxstat.rs_rssi_ctl2 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt02); + ds->ds_rxstat.rs_rssi_ext0 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt10); + ds->ds_rxstat.rs_rssi_ext1 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt11); + ds->ds_rxstat.rs_rssi_ext2 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt12); + if (ads.ds_rxstatus8 & AR_RxKeyIdxValid) + ds->ds_rxstat.rs_keyix = MS(ads.ds_rxstatus8, AR_KeyIdx); + else + ds->ds_rxstat.rs_keyix = ATH9K_RXKEYIX_INVALID; + + ds->ds_rxstat.rs_rate = RXSTATUS_RATE(ah, (&ads)); + ds->ds_rxstat.rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0; + + ds->ds_rxstat.rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0; + ds->ds_rxstat.rs_moreaggr = + (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0; + ds->ds_rxstat.rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna); + ds->ds_rxstat.rs_flags = + (ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0; + ds->ds_rxstat.rs_flags |= + (ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0; + + if (ads.ds_rxstatus8 & AR_PreDelimCRCErr) + ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_PRE; + if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) + ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_POST; + if (ads.ds_rxstatus8 & AR_DecryptBusyErr) + ds->ds_rxstat.rs_flags |= ATH9K_RX_DECRYPT_BUSY; + + if ((ads.ds_rxstatus8 & AR_RxFrameOK) == 0) { + + if (ads.ds_rxstatus8 & AR_CRCErr) + ds->ds_rxstat.rs_status |= ATH9K_RXERR_CRC; + else if (ads.ds_rxstatus8 & AR_PHYErr) { + u32 phyerr; + + ds->ds_rxstat.rs_status |= ATH9K_RXERR_PHY; + phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode); + ds->ds_rxstat.rs_phyerr = phyerr; + } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr) + ds->ds_rxstat.rs_status |= ATH9K_RXERR_DECRYPT; + else if (ads.ds_rxstatus8 & AR_MichaelErr) + ds->ds_rxstat.rs_status |= ATH9K_RXERR_MIC; + } + + return 0; +} + +static void ath9k_hw_setup_rate_table(struct ath_hal *ah, + struct ath9k_rate_table *rt) +{ + int i; + + if (rt->rateCodeToIndex[0] != 0) + return; + for (i = 0; i < 256; i++) + rt->rateCodeToIndex[i] = (u8) -1; + for (i = 0; i < rt->rateCount; i++) { + u8 code = rt->info[i].rateCode; + u8 cix = rt->info[i].controlRate; + + rt->rateCodeToIndex[code] = i; + rt->rateCodeToIndex[code | rt->info[i].shortPreamble] = i; + + rt->info[i].lpAckDuration = + ath9k_hw_computetxtime(ah, rt, + WLAN_CTRL_FRAME_SIZE, + cix, + false); + rt->info[i].spAckDuration = + ath9k_hw_computetxtime(ah, rt, + WLAN_CTRL_FRAME_SIZE, + cix, + true); + } +} + +const struct ath9k_rate_table *ath9k_hw_getratetable(struct ath_hal *ah, + u32 mode) +{ + struct ath9k_rate_table *rt; + switch (mode) { + case ATH9K_MODE_SEL_11A: + rt = &ar5416_11a_table; + break; + case ATH9K_MODE_SEL_11B: + rt = &ar5416_11b_table; + break; + case ATH9K_MODE_SEL_11G: + rt = &ar5416_11g_table; + break; + case ATH9K_MODE_SEL_11NG_HT20: + case ATH9K_MODE_SEL_11NG_HT40PLUS: + case ATH9K_MODE_SEL_11NG_HT40MINUS: + rt = &ar5416_11ng_table; + break; + case ATH9K_MODE_SEL_11NA_HT20: + case ATH9K_MODE_SEL_11NA_HT40PLUS: + case ATH9K_MODE_SEL_11NA_HT40MINUS: + rt = &ar5416_11na_table; + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, "%s: invalid mode 0x%x\n", + __func__, mode); + return NULL; + } + ath9k_hw_setup_rate_table(ah, rt); + return rt; +} + +static const char *ath9k_hw_devname(u16 devid) +{ + switch (devid) { + case AR5416_DEVID_PCI: + case AR5416_DEVID_PCIE: + return "Atheros 5416"; + case AR9160_DEVID_PCI: + return "Atheros 9160"; + case AR9280_DEVID_PCI: + case AR9280_DEVID_PCIE: + return "Atheros 9280"; + } + return NULL; +} + +const char *ath9k_hw_probe(u16 vendorid, u16 devid) +{ + return vendorid == ATHEROS_VENDOR_ID ? + ath9k_hw_devname(devid) : NULL; +} + +struct ath_hal *ath9k_hw_attach(u16 devid, + struct ath_softc *sc, + void __iomem *mem, + int *error) +{ + struct ath_hal *ah = NULL; + + switch (devid) { + case AR5416_DEVID_PCI: + case AR5416_DEVID_PCIE: + case AR9160_DEVID_PCI: + case AR9280_DEVID_PCI: + case AR9280_DEVID_PCIE: + ah = ath9k_hw_do_attach(devid, sc, mem, error); + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "devid=0x%x not supported.\n", devid); + ah = NULL; + *error = -ENXIO; + break; + } + if (ah != NULL) { + ah->ah_devid = ah->ah_devid; + ah->ah_subvendorid = ah->ah_subvendorid; + ah->ah_macVersion = ah->ah_macVersion; + ah->ah_macRev = ah->ah_macRev; + ah->ah_phyRev = ah->ah_phyRev; + ah->ah_analog5GhzRev = ah->ah_analog5GhzRev; + ah->ah_analog2GhzRev = ah->ah_analog2GhzRev; + } + return ah; +} + +u16 +ath9k_hw_computetxtime(struct ath_hal *ah, + const struct ath9k_rate_table *rates, + u32 frameLen, u16 rateix, + bool shortPreamble) +{ + u32 bitsPerSymbol, numBits, numSymbols, phyTime, txTime; + u32 kbps; + + kbps = rates->info[rateix].rateKbps; + + if (kbps == 0) + return 0; + switch (rates->info[rateix].phy) { + + case PHY_CCK: + phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; + if (shortPreamble && rates->info[rateix].shortPreamble) + phyTime >>= 1; + numBits = frameLen << 3; + txTime = CCK_SIFS_TIME + phyTime + + ((numBits * 1000) / kbps); + break; + case PHY_OFDM: + if (ah->ah_curchan && IS_CHAN_QUARTER_RATE(ah->ah_curchan)) { + bitsPerSymbol = + (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000; + + numBits = OFDM_PLCP_BITS + (frameLen << 3); + numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); + txTime = OFDM_SIFS_TIME_QUARTER + + OFDM_PREAMBLE_TIME_QUARTER + + (numSymbols * OFDM_SYMBOL_TIME_QUARTER); + } else if (ah->ah_curchan && + IS_CHAN_HALF_RATE(ah->ah_curchan)) { + bitsPerSymbol = + (kbps * OFDM_SYMBOL_TIME_HALF) / 1000; + + numBits = OFDM_PLCP_BITS + (frameLen << 3); + numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); + txTime = OFDM_SIFS_TIME_HALF + + OFDM_PREAMBLE_TIME_HALF + + (numSymbols * OFDM_SYMBOL_TIME_HALF); + } else { + bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000; + + numBits = OFDM_PLCP_BITS + (frameLen << 3); + numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); + txTime = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME + + (numSymbols * OFDM_SYMBOL_TIME); + } + break; + + default: + DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO, + "%s: unknown phy %u (rate ix %u)\n", __func__, + rates->info[rateix].phy, rateix); + txTime = 0; + break; + } + return txTime; +} + +u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags) +{ + if (flags & CHANNEL_2GHZ) { + if (freq == 2484) + return 14; + if (freq < 2484) + return (freq - 2407) / 5; + else + return 15 + ((freq - 2512) / 20); + } else if (flags & CHANNEL_5GHZ) { + if (ath9k_regd_is_public_safety_sku(ah) && + IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) { + return ((freq * 10) + + (((freq % 5) == 2) ? 5 : 0) - 49400) / 5; + } else if ((flags & CHANNEL_A) && (freq <= 5000)) { + return (freq - 4000) / 5; + } else { + return (freq - 5000) / 5; + } + } else { + if (freq == 2484) + return 14; + if (freq < 2484) + return (freq - 2407) / 5; + if (freq < 5000) { + if (ath9k_regd_is_public_safety_sku(ah) + && IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) { + return ((freq * 10) + + (((freq % 5) == + 2) ? 5 : 0) - 49400) / 5; + } else if (freq > 4900) { + return (freq - 4000) / 5; + } else { + return 15 + ((freq - 2512) / 20); + } + } + return (freq - 5000) / 5; + } +} + +int16_t +ath9k_hw_getchan_noise(struct ath_hal *ah, struct ath9k_channel *chan) +{ + struct ath9k_channel *ichan; + + ichan = ath9k_regd_check_channel(ah, chan); + if (ichan == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channelFlags); + return 0; + } + if (ichan->rawNoiseFloor == 0) { + enum wireless_mode mode = ath9k_hw_chan2wmode(ah, chan); + return NOISE_FLOOR[mode]; + } else + return ichan->rawNoiseFloor; +} + +bool ath9k_hw_set_tsfadjust(struct ath_hal *ah, u32 setting) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (setting) + ahp->ah_miscMode |= AR_PCU_TX_ADD_TSF; + else + ahp->ah_miscMode &= ~AR_PCU_TX_ADD_TSF; + return true; +} + +bool ath9k_hw_phycounters(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + return ahp->ah_hasHwPhyCounters ? true : false; +} + +u32 ath9k_hw_gettxbuf(struct ath_hal *ah, u32 q) +{ + return REG_READ(ah, AR_QTXDP(q)); +} + +bool ath9k_hw_puttxbuf(struct ath_hal *ah, u32 q, + u32 txdp) +{ + REG_WRITE(ah, AR_QTXDP(q), txdp); + + return true; +} + +bool ath9k_hw_txstart(struct ath_hal *ah, u32 q) +{ + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: queue %u\n", __func__, q); + + REG_WRITE(ah, AR_Q_TXE, 1 << q); + + return true; +} + +u32 ath9k_hw_numtxpending(struct ath_hal *ah, u32 q) +{ + u32 npend; + + npend = REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT; + if (npend == 0) { + + if (REG_READ(ah, AR_Q_TXE) & (1 << q)) + npend = 1; + } + return npend; +} + +bool ath9k_hw_stoptxdma(struct ath_hal *ah, u32 q) +{ + u32 wait; + + REG_WRITE(ah, AR_Q_TXD, 1 << q); + + for (wait = 1000; wait != 0; wait--) { + if (ath9k_hw_numtxpending(ah, q) == 0) + break; + udelay(100); + } + + if (ath9k_hw_numtxpending(ah, q)) { + u32 tsfLow, j; + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: Num of pending TX Frames %d on Q %d\n", + __func__, ath9k_hw_numtxpending(ah, q), q); + + for (j = 0; j < 2; j++) { + tsfLow = REG_READ(ah, AR_TSF_L32); + REG_WRITE(ah, AR_QUIET2, + SM(10, AR_QUIET2_QUIET_DUR)); + REG_WRITE(ah, AR_QUIET_PERIOD, 100); + REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsfLow >> 10); + REG_SET_BIT(ah, AR_TIMER_MODE, + AR_QUIET_TIMER_EN); + + if ((REG_READ(ah, AR_TSF_L32) >> 10) == + (tsfLow >> 10)) { + break; + } + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: TSF have moved while trying to set " + "quiet time TSF: 0x%08x\n", + __func__, tsfLow); + } + + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); + + udelay(200); + REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN); + + wait = 1000; + + while (ath9k_hw_numtxpending(ah, q)) { + if ((--wait) == 0) { + DPRINTF(ah->ah_sc, ATH_DBG_XMIT, + "%s: Failed to stop Tx DMA in 100 " + "msec after killing last frame\n", + __func__); + break; + } + udelay(100); + } + + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); + } + + REG_WRITE(ah, AR_Q_TXD, 0); + return wait != 0; +} diff --git a/drivers/net/wireless/ath9k/hw.h b/drivers/net/wireless/ath9k/hw.h new file mode 100644 index 0000000..ae680f2 --- /dev/null +++ b/drivers/net/wireless/ath9k/hw.h @@ -0,0 +1,969 @@ +/* + * 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. + */ + +#ifndef HW_H +#define HW_H + +#include +#include + +struct ar5416_desc { + u32 ds_link; + u32 ds_data; + u32 ds_ctl0; + u32 ds_ctl1; + union { + struct { + u32 ctl2; + u32 ctl3; + u32 ctl4; + u32 ctl5; + u32 ctl6; + u32 ctl7; + u32 ctl8; + u32 ctl9; + u32 ctl10; + u32 ctl11; + u32 status0; + u32 status1; + u32 status2; + u32 status3; + u32 status4; + u32 status5; + u32 status6; + u32 status7; + u32 status8; + u32 status9; + } tx; + struct { + u32 status0; + u32 status1; + u32 status2; + u32 status3; + u32 status4; + u32 status5; + u32 status6; + u32 status7; + u32 status8; + } rx; + } u; +} __packed; + +#define AR5416DESC(_ds) ((struct ar5416_desc *)(_ds)) +#define AR5416DESC_CONST(_ds) ((const struct ar5416_desc *)(_ds)) + +#define ds_ctl2 u.tx.ctl2 +#define ds_ctl3 u.tx.ctl3 +#define ds_ctl4 u.tx.ctl4 +#define ds_ctl5 u.tx.ctl5 +#define ds_ctl6 u.tx.ctl6 +#define ds_ctl7 u.tx.ctl7 +#define ds_ctl8 u.tx.ctl8 +#define ds_ctl9 u.tx.ctl9 +#define ds_ctl10 u.tx.ctl10 +#define ds_ctl11 u.tx.ctl11 + +#define ds_txstatus0 u.tx.status0 +#define ds_txstatus1 u.tx.status1 +#define ds_txstatus2 u.tx.status2 +#define ds_txstatus3 u.tx.status3 +#define ds_txstatus4 u.tx.status4 +#define ds_txstatus5 u.tx.status5 +#define ds_txstatus6 u.tx.status6 +#define ds_txstatus7 u.tx.status7 +#define ds_txstatus8 u.tx.status8 +#define ds_txstatus9 u.tx.status9 + +#define ds_rxstatus0 u.rx.status0 +#define ds_rxstatus1 u.rx.status1 +#define ds_rxstatus2 u.rx.status2 +#define ds_rxstatus3 u.rx.status3 +#define ds_rxstatus4 u.rx.status4 +#define ds_rxstatus5 u.rx.status5 +#define ds_rxstatus6 u.rx.status6 +#define ds_rxstatus7 u.rx.status7 +#define ds_rxstatus8 u.rx.status8 + +#define AR_FrameLen 0x00000fff +#define AR_VirtMoreFrag 0x00001000 +#define AR_TxCtlRsvd00 0x0000e000 +#define AR_XmitPower 0x003f0000 +#define AR_XmitPower_S 16 +#define AR_RTSEnable 0x00400000 +#define AR_VEOL 0x00800000 +#define AR_ClrDestMask 0x01000000 +#define AR_TxCtlRsvd01 0x1e000000 +#define AR_TxIntrReq 0x20000000 +#define AR_DestIdxValid 0x40000000 +#define AR_CTSEnable 0x80000000 + +#define AR_BufLen 0x00000fff +#define AR_TxMore 0x00001000 +#define AR_DestIdx 0x000fe000 +#define AR_DestIdx_S 13 +#define AR_FrameType 0x00f00000 +#define AR_FrameType_S 20 +#define AR_NoAck 0x01000000 +#define AR_InsertTS 0x02000000 +#define AR_CorruptFCS 0x04000000 +#define AR_ExtOnly 0x08000000 +#define AR_ExtAndCtl 0x10000000 +#define AR_MoreAggr 0x20000000 +#define AR_IsAggr 0x40000000 + +#define AR_BurstDur 0x00007fff +#define AR_BurstDur_S 0 +#define AR_DurUpdateEna 0x00008000 +#define AR_XmitDataTries0 0x000f0000 +#define AR_XmitDataTries0_S 16 +#define AR_XmitDataTries1 0x00f00000 +#define AR_XmitDataTries1_S 20 +#define AR_XmitDataTries2 0x0f000000 +#define AR_XmitDataTries2_S 24 +#define AR_XmitDataTries3 0xf0000000 +#define AR_XmitDataTries3_S 28 + +#define AR_XmitRate0 0x000000ff +#define AR_XmitRate0_S 0 +#define AR_XmitRate1 0x0000ff00 +#define AR_XmitRate1_S 8 +#define AR_XmitRate2 0x00ff0000 +#define AR_XmitRate2_S 16 +#define AR_XmitRate3 0xff000000 +#define AR_XmitRate3_S 24 + +#define AR_PacketDur0 0x00007fff +#define AR_PacketDur0_S 0 +#define AR_RTSCTSQual0 0x00008000 +#define AR_PacketDur1 0x7fff0000 +#define AR_PacketDur1_S 16 +#define AR_RTSCTSQual1 0x80000000 + +#define AR_PacketDur2 0x00007fff +#define AR_PacketDur2_S 0 +#define AR_RTSCTSQual2 0x00008000 +#define AR_PacketDur3 0x7fff0000 +#define AR_PacketDur3_S 16 +#define AR_RTSCTSQual3 0x80000000 + +#define AR_AggrLen 0x0000ffff +#define AR_AggrLen_S 0 +#define AR_TxCtlRsvd60 0x00030000 +#define AR_PadDelim 0x03fc0000 +#define AR_PadDelim_S 18 +#define AR_EncrType 0x0c000000 +#define AR_EncrType_S 26 +#define AR_TxCtlRsvd61 0xf0000000 + +#define AR_2040_0 0x00000001 +#define AR_GI0 0x00000002 +#define AR_ChainSel0 0x0000001c +#define AR_ChainSel0_S 2 +#define AR_2040_1 0x00000020 +#define AR_GI1 0x00000040 +#define AR_ChainSel1 0x00000380 +#define AR_ChainSel1_S 7 +#define AR_2040_2 0x00000400 +#define AR_GI2 0x00000800 +#define AR_ChainSel2 0x00007000 +#define AR_ChainSel2_S 12 +#define AR_2040_3 0x00008000 +#define AR_GI3 0x00010000 +#define AR_ChainSel3 0x000e0000 +#define AR_ChainSel3_S 17 +#define AR_RTSCTSRate 0x0ff00000 +#define AR_RTSCTSRate_S 20 +#define AR_TxCtlRsvd70 0xf0000000 + +#define AR_TxRSSIAnt00 0x000000ff +#define AR_TxRSSIAnt00_S 0 +#define AR_TxRSSIAnt01 0x0000ff00 +#define AR_TxRSSIAnt01_S 8 +#define AR_TxRSSIAnt02 0x00ff0000 +#define AR_TxRSSIAnt02_S 16 +#define AR_TxStatusRsvd00 0x3f000000 +#define AR_TxBaStatus 0x40000000 +#define AR_TxStatusRsvd01 0x80000000 + +#define AR_FrmXmitOK 0x00000001 +#define AR_ExcessiveRetries 0x00000002 +#define AR_FIFOUnderrun 0x00000004 +#define AR_Filtered 0x00000008 +#define AR_RTSFailCnt 0x000000f0 +#define AR_RTSFailCnt_S 4 +#define AR_DataFailCnt 0x00000f00 +#define AR_DataFailCnt_S 8 +#define AR_VirtRetryCnt 0x0000f000 +#define AR_VirtRetryCnt_S 12 +#define AR_TxDelimUnderrun 0x00010000 +#define AR_TxDataUnderrun 0x00020000 +#define AR_DescCfgErr 0x00040000 +#define AR_TxTimerExpired 0x00080000 +#define AR_TxStatusRsvd10 0xfff00000 + +#define AR_SendTimestamp ds_txstatus2 +#define AR_BaBitmapLow ds_txstatus3 +#define AR_BaBitmapHigh ds_txstatus4 + +#define AR_TxRSSIAnt10 0x000000ff +#define AR_TxRSSIAnt10_S 0 +#define AR_TxRSSIAnt11 0x0000ff00 +#define AR_TxRSSIAnt11_S 8 +#define AR_TxRSSIAnt12 0x00ff0000 +#define AR_TxRSSIAnt12_S 16 +#define AR_TxRSSICombined 0xff000000 +#define AR_TxRSSICombined_S 24 + +#define AR_TxEVM0 ds_txstatus5 +#define AR_TxEVM1 ds_txstatus6 +#define AR_TxEVM2 ds_txstatus7 + +#define AR_TxDone 0x00000001 +#define AR_SeqNum 0x00001ffe +#define AR_SeqNum_S 1 +#define AR_TxStatusRsvd80 0x0001e000 +#define AR_TxOpExceeded 0x00020000 +#define AR_TxStatusRsvd81 0x001c0000 +#define AR_FinalTxIdx 0x00600000 +#define AR_FinalTxIdx_S 21 +#define AR_TxStatusRsvd82 0x01800000 +#define AR_PowerMgmt 0x02000000 +#define AR_TxStatusRsvd83 0xfc000000 + +#define AR_RxCTLRsvd00 0xffffffff + +#define AR_BufLen 0x00000fff +#define AR_RxCtlRsvd00 0x00001000 +#define AR_RxIntrReq 0x00002000 +#define AR_RxCtlRsvd01 0xffffc000 + +#define AR_RxRSSIAnt00 0x000000ff +#define AR_RxRSSIAnt00_S 0 +#define AR_RxRSSIAnt01 0x0000ff00 +#define AR_RxRSSIAnt01_S 8 +#define AR_RxRSSIAnt02 0x00ff0000 +#define AR_RxRSSIAnt02_S 16 +#define AR_RxRate 0xff000000 +#define AR_RxRate_S 24 +#define AR_RxStatusRsvd00 0xff000000 + +#define AR_DataLen 0x00000fff +#define AR_RxMore 0x00001000 +#define AR_NumDelim 0x003fc000 +#define AR_NumDelim_S 14 +#define AR_RxStatusRsvd10 0xff800000 + +#define AR_RcvTimestamp ds_rxstatus2 + +#define AR_GI 0x00000001 +#define AR_2040 0x00000002 +#define AR_Parallel40 0x00000004 +#define AR_Parallel40_S 2 +#define AR_RxStatusRsvd30 0x000000f8 +#define AR_RxAntenna 0xffffff00 +#define AR_RxAntenna_S 8 + +#define AR_RxRSSIAnt10 0x000000ff +#define AR_RxRSSIAnt10_S 0 +#define AR_RxRSSIAnt11 0x0000ff00 +#define AR_RxRSSIAnt11_S 8 +#define AR_RxRSSIAnt12 0x00ff0000 +#define AR_RxRSSIAnt12_S 16 +#define AR_RxRSSICombined 0xff000000 +#define AR_RxRSSICombined_S 24 + +#define AR_RxEVM0 ds_rxstatus4 +#define AR_RxEVM1 ds_rxstatus5 +#define AR_RxEVM2 ds_rxstatus6 + +#define AR_RxDone 0x00000001 +#define AR_RxFrameOK 0x00000002 +#define AR_CRCErr 0x00000004 +#define AR_DecryptCRCErr 0x00000008 +#define AR_PHYErr 0x00000010 +#define AR_MichaelErr 0x00000020 +#define AR_PreDelimCRCErr 0x00000040 +#define AR_RxStatusRsvd70 0x00000080 +#define AR_RxKeyIdxValid 0x00000100 +#define AR_KeyIdx 0x0000fe00 +#define AR_KeyIdx_S 9 +#define AR_PHYErrCode 0x0000ff00 +#define AR_PHYErrCode_S 8 +#define AR_RxMoreAggr 0x00010000 +#define AR_RxAggr 0x00020000 +#define AR_PostDelimCRCErr 0x00040000 +#define AR_RxStatusRsvd71 0x3ff80000 +#define AR_DecryptBusyErr 0x40000000 +#define AR_KeyMiss 0x80000000 + +#define AR5416_MAGIC 0x19641014 + +#define RXSTATUS_RATE(ah, ads) (AR_SREV_5416_V20_OR_LATER(ah) ? \ + MS(ads->ds_rxstatus0, AR_RxRate) : \ + (ads->ds_rxstatus3 >> 2) & 0xFF) +#define RXSTATUS_DUPLICATE(ah, ads) (AR_SREV_5416_V20_OR_LATER(ah) ? \ + MS(ads->ds_rxstatus3, AR_Parallel40) : \ + (ads->ds_rxstatus3 >> 10) & 0x1) + +#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 & ATH9K_RATESERIES_RTS_CTS ? \ + AR_RTSCTSQual##_index : 0)) + +#define set11nRateFlags(_series, _index) \ + (((_series)[_index].RateFlags & ATH9K_RATESERIES_2040 ? \ + AR_2040_##_index : 0) \ + |((_series)[_index].RateFlags & ATH9K_RATESERIES_HALFGI ? \ + AR_GI##_index : 0) \ + |SM((_series)[_index].ChSel, AR_ChainSel##_index)) + +#define AR_SREV_9100(ah) ((ah->ah_macVersion) == AR_SREV_VERSION_9100) + +#define INIT_CONFIG_STATUS 0x00000000 +#define INIT_RSSI_THR 0x00000700 +#define INIT_BCON_CNTRL_REG 0x00000000 + +#define MIN_TX_FIFO_THRESHOLD 0x1 +#define MAX_TX_FIFO_THRESHOLD ((4096 / 64) - 1) +#define INIT_TX_FIFO_THRESHOLD MIN_TX_FIFO_THRESHOLD + +#define NUM_CORNER_FIX_BITS_2133 7 +#define CCK_OFDM_GAIN_DELTA 15 + +struct ar5416AniState { + struct ath9k_channel c; + u8 noiseImmunityLevel; + u8 spurImmunityLevel; + u8 firstepLevel; + u8 ofdmWeakSigDetectOff; + u8 cckWeakSigThreshold; + u32 listenTime; + u32 ofdmTrigHigh; + u32 ofdmTrigLow; + int32_t cckTrigHigh; + int32_t cckTrigLow; + int32_t rssiThrLow; + int32_t rssiThrHigh; + u32 noiseFloor; + u32 txFrameCount; + u32 rxFrameCount; + u32 cycleCount; + u32 ofdmPhyErrCount; + u32 cckPhyErrCount; + u32 ofdmPhyErrBase; + u32 cckPhyErrBase; + int16_t pktRssi[2]; + int16_t ofdmErrRssi[2]; + int16_t cckErrRssi[2]; +}; + +#define HAL_PROCESS_ANI 0x00000001 +#define HAL_RADAR_EN 0x80000000 +#define HAL_AR_EN 0x40000000 + +#define DO_ANI(ah) \ + ((AH5416(ah)->ah_procPhyErr & HAL_PROCESS_ANI)) + +struct ar5416Stats { + u32 ast_ani_niup; + u32 ast_ani_nidown; + u32 ast_ani_spurup; + u32 ast_ani_spurdown; + u32 ast_ani_ofdmon; + u32 ast_ani_ofdmoff; + u32 ast_ani_cckhigh; + u32 ast_ani_ccklow; + u32 ast_ani_stepup; + u32 ast_ani_stepdown; + u32 ast_ani_ofdmerrs; + u32 ast_ani_cckerrs; + u32 ast_ani_reset; + u32 ast_ani_lzero; + u32 ast_ani_lneg; + struct ath9k_mib_stats ast_mibstats; + struct ath9k_node_stats ast_nodestats; +}; + +#define AR5416_OPFLAGS_11A 0x01 +#define AR5416_OPFLAGS_11G 0x02 +#define AR5416_OPFLAGS_N_5G_HT40 0x04 +#define AR5416_OPFLAGS_N_2G_HT40 0x08 +#define AR5416_OPFLAGS_N_5G_HT20 0x10 +#define AR5416_OPFLAGS_N_2G_HT20 0x20 + +#define EEP_RFSILENT_ENABLED 0x0001 +#define EEP_RFSILENT_ENABLED_S 0 +#define EEP_RFSILENT_POLARITY 0x0002 +#define EEP_RFSILENT_POLARITY_S 1 +#define EEP_RFSILENT_GPIO_SEL 0x001c +#define EEP_RFSILENT_GPIO_SEL_S 2 + +#define AR5416_EEP_NO_BACK_VER 0x1 +#define AR5416_EEP_VER 0xE +#define AR5416_EEP_VER_MINOR_MASK 0x0FFF +#define AR5416_EEP_MINOR_VER_2 0x2 +#define AR5416_EEP_MINOR_VER_3 0x3 +#define AR5416_EEP_MINOR_VER_7 0x7 +#define AR5416_EEP_MINOR_VER_9 0x9 + +#define AR5416_EEP_START_LOC 256 +#define AR5416_NUM_5G_CAL_PIERS 8 +#define AR5416_NUM_2G_CAL_PIERS 4 +#define AR5416_NUM_5G_20_TARGET_POWERS 8 +#define AR5416_NUM_5G_40_TARGET_POWERS 8 +#define AR5416_NUM_2G_CCK_TARGET_POWERS 3 +#define AR5416_NUM_2G_20_TARGET_POWERS 4 +#define AR5416_NUM_2G_40_TARGET_POWERS 4 +#define AR5416_NUM_CTLS 24 +#define AR5416_NUM_BAND_EDGES 8 +#define AR5416_NUM_PD_GAINS 4 +#define AR5416_PD_GAINS_IN_MASK 4 +#define AR5416_PD_GAIN_ICEPTS 5 +#define AR5416_EEPROM_MODAL_SPURS 5 +#define AR5416_MAX_RATE_POWER 63 +#define AR5416_NUM_PDADC_VALUES 128 +#define AR5416_NUM_RATES 16 +#define AR5416_BCHAN_UNUSED 0xFF +#define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64 +#define AR5416_EEPMISC_BIG_ENDIAN 0x01 +#define AR5416_MAX_CHAINS 3 +#define AR5416_ANT_16S 25 + +#define AR5416_NUM_ANT_CHAIN_FIELDS 7 +#define AR5416_NUM_ANT_COMMON_FIELDS 4 +#define AR5416_SIZE_ANT_CHAIN_FIELD 3 +#define AR5416_SIZE_ANT_COMMON_FIELD 4 +#define AR5416_ANT_CHAIN_MASK 0x7 +#define AR5416_ANT_COMMON_MASK 0xf +#define AR5416_CHAIN_0_IDX 0 +#define AR5416_CHAIN_1_IDX 1 +#define AR5416_CHAIN_2_IDX 2 + +#define AR5416_PWR_TABLE_OFFSET -5 +#define AR5416_LEGACY_CHAINMASK 1 + +enum eeprom_param { + EEP_NFTHRESH_5, + EEP_NFTHRESH_2, + EEP_MAC_MSW, + EEP_MAC_MID, + EEP_MAC_LSW, + EEP_REG_0, + EEP_REG_1, + EEP_OP_CAP, + EEP_OP_MODE, + EEP_RF_SILENT, + EEP_OB_5, + EEP_DB_5, + EEP_OB_2, + EEP_DB_2, + EEP_MINOR_REV, + EEP_TX_MASK, + EEP_RX_MASK, +}; + +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 +}; + +struct base_eep_header { + u16 length; + u16 checksum; + u16 version; + u8 opCapFlags; + u8 eepMisc; + u16 regDmn[2]; + u8 macAddr[6]; + u8 rxMask; + u8 txMask; + u16 rfSilent; + u16 blueToothOptions; + u16 deviceCap; + u32 binBuildNumber; + u8 deviceType; + u8 pwdclkind; + u8 futureBase[32]; +} __packed; + +struct spur_chan { + u16 spurChan; + u8 spurRangeLow; + u8 spurRangeHigh; +} __packed; + +struct modal_eep_header { + u32 antCtrlChain[AR5416_MAX_CHAINS]; + u32 antCtrlCommon; + u8 antennaGainCh[AR5416_MAX_CHAINS]; + u8 switchSettling; + u8 txRxAttenCh[AR5416_MAX_CHAINS]; + u8 rxTxMarginCh[AR5416_MAX_CHAINS]; + u8 adcDesiredSize; + u8 pgaDesiredSize; + u8 xlnaGainCh[AR5416_MAX_CHAINS]; + u8 txEndToXpaOff; + u8 txEndToRxOn; + u8 txFrameToXpaOn; + u8 thresh62; + u8 noiseFloorThreshCh[AR5416_MAX_CHAINS]; + u8 xpdGain; + u8 xpd; + u8 iqCalICh[AR5416_MAX_CHAINS]; + u8 iqCalQCh[AR5416_MAX_CHAINS]; + u8 pdGainOverlap; + u8 ob; + u8 db; + u8 xpaBiasLvl; + u8 pwrDecreaseFor2Chain; + u8 pwrDecreaseFor3Chain; + u8 txFrameToDataStart; + u8 txFrameToPaOn; + u8 ht40PowerIncForPdadc; + u8 bswAtten[AR5416_MAX_CHAINS]; + u8 bswMargin[AR5416_MAX_CHAINS]; + u8 swSettleHt40; + u8 xatten2Db[AR5416_MAX_CHAINS]; + u8 xatten2Margin[AR5416_MAX_CHAINS]; + u8 ob_ch1; + u8 db_ch1; + u8 useAnt1:1, + force_xpaon:1, + local_bias:1, + femBandSelectUsed:1, xlnabufin:1, xlnaisel:2, xlnabufmode:1; + u8 futureModalar9280; + u16 xpaBiasLvlFreq[3]; + u8 futureModal[6]; + + struct spur_chan spurChans[AR5416_EEPROM_MODAL_SPURS]; +} __packed; + +struct cal_data_per_freq { + u8 pwrPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; + u8 vpdPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; +} __packed; + +struct cal_target_power_leg { + u8 bChannel; + u8 tPow2x[4]; +} __packed; + +struct cal_target_power_ht { + u8 bChannel; + u8 tPow2x[8]; +} __packed; + +#ifdef __BIG_ENDIAN_BITFIELD +struct cal_ctl_edges { + u8 bChannel; + u8 flag:2, tPower:6; +} __packed; +#else +struct cal_ctl_edges { + u8 bChannel; + u8 tPower:6, flag:2; +} __packed; +#endif + +struct cal_ctl_data { + struct cal_ctl_edges + ctlEdges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES]; +} __packed; + +struct ar5416_eeprom { + struct base_eep_header baseEepHeader; + u8 custData[64]; + struct modal_eep_header modalHeader[2]; + u8 calFreqPier5G[AR5416_NUM_5G_CAL_PIERS]; + u8 calFreqPier2G[AR5416_NUM_2G_CAL_PIERS]; + struct cal_data_per_freq + calPierData5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS]; + struct cal_data_per_freq + calPierData2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS]; + struct cal_target_power_leg + calTargetPower5G[AR5416_NUM_5G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower5GHT20[AR5416_NUM_5G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower5GHT40[AR5416_NUM_5G_40_TARGET_POWERS]; + struct cal_target_power_leg + calTargetPowerCck[AR5416_NUM_2G_CCK_TARGET_POWERS]; + struct cal_target_power_leg + calTargetPower2G[AR5416_NUM_2G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower2GHT20[AR5416_NUM_2G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower2GHT40[AR5416_NUM_2G_40_TARGET_POWERS]; + u8 ctlIndex[AR5416_NUM_CTLS]; + struct cal_ctl_data ctlData[AR5416_NUM_CTLS]; + u8 padding; +} __packed; + +struct ar5416IniArray { + u32 *ia_array; + u32 ia_rows; + u32 ia_columns; +}; + +#define INIT_INI_ARRAY(iniarray, array, rows, columns) do { \ + (iniarray)->ia_array = (u32 *)(array); \ + (iniarray)->ia_rows = (rows); \ + (iniarray)->ia_columns = (columns); \ + } while (0) + +#define INI_RA(iniarray, row, column) \ + (((iniarray)->ia_array)[(row) * ((iniarray)->ia_columns) + (column)]) + +#define INIT_CAL(_perCal) do { \ + (_perCal)->calState = CAL_WAITING; \ + (_perCal)->calNext = NULL; \ + } while (0) + +#define INSERT_CAL(_ahp, _perCal) \ + do { \ + if ((_ahp)->ah_cal_list_last == NULL) { \ + (_ahp)->ah_cal_list = \ + (_ahp)->ah_cal_list_last = (_perCal); \ + ((_ahp)->ah_cal_list_last)->calNext = (_perCal); \ + } else { \ + ((_ahp)->ah_cal_list_last)->calNext = (_perCal); \ + (_ahp)->ah_cal_list_last = (_perCal); \ + (_perCal)->calNext = (_ahp)->ah_cal_list; \ + } \ + } while (0) + +enum hal_cal_types { + ADC_DC_INIT_CAL = 0x1, + ADC_GAIN_CAL = 0x2, + ADC_DC_CAL = 0x4, + IQ_MISMATCH_CAL = 0x8 +}; + +enum hal_cal_state { + CAL_INACTIVE, + CAL_WAITING, + CAL_RUNNING, + CAL_DONE +}; + +#define MIN_CAL_SAMPLES 1 +#define MAX_CAL_SAMPLES 64 +#define INIT_LOG_COUNT 5 +#define PER_MIN_LOG_COUNT 2 +#define PER_MAX_LOG_COUNT 10 + +struct hal_percal_data { + enum hal_cal_types calType; + u32 calNumSamples; + u32 calCountMax; + void (*calCollect) (struct ath_hal *); + void (*calPostProc) (struct ath_hal *, u8); +}; + +struct hal_cal_list { + const struct hal_percal_data *calData; + enum hal_cal_state calState; + struct hal_cal_list *calNext; +}; + +struct ath_hal_5416 { + struct ath_hal ah; + struct ar5416_eeprom ah_eeprom; + u8 ah_macaddr[ETH_ALEN]; + u8 ah_bssid[ETH_ALEN]; + u8 ah_bssidmask[ETH_ALEN]; + u16 ah_assocId; + int16_t ah_curchanRadIndex; + u32 ah_maskReg; + struct ar5416Stats ah_stats; + u32 ah_txDescMask; + u32 ah_txOkInterruptMask; + u32 ah_txErrInterruptMask; + u32 ah_txDescInterruptMask; + u32 ah_txEolInterruptMask; + u32 ah_txUrnInterruptMask; + struct ath9k_tx_queue_info ah_txq[ATH9K_NUM_TX_QUEUES]; + enum ath9k_power_mode ah_powerMode; + bool ah_chipFullSleep; + u32 ah_atimWindow; + enum ath9k_ant_setting ah_diversityControl; + u16 ah_antennaSwitchSwap; + enum hal_cal_types ah_suppCals; + struct hal_cal_list ah_iqCalData; + struct hal_cal_list ah_adcGainCalData; + struct hal_cal_list ah_adcDcCalInitData; + struct hal_cal_list ah_adcDcCalData; + struct hal_cal_list *ah_cal_list; + struct hal_cal_list *ah_cal_list_last; + struct hal_cal_list *ah_cal_list_curr; +#define ah_totalPowerMeasI ah_Meas0.unsign +#define ah_totalPowerMeasQ ah_Meas1.unsign +#define ah_totalIqCorrMeas ah_Meas2.sign +#define ah_totalAdcIOddPhase ah_Meas0.unsign +#define ah_totalAdcIEvenPhase ah_Meas1.unsign +#define ah_totalAdcQOddPhase ah_Meas2.unsign +#define ah_totalAdcQEvenPhase ah_Meas3.unsign +#define ah_totalAdcDcOffsetIOddPhase ah_Meas0.sign +#define ah_totalAdcDcOffsetIEvenPhase ah_Meas1.sign +#define ah_totalAdcDcOffsetQOddPhase ah_Meas2.sign +#define ah_totalAdcDcOffsetQEvenPhase ah_Meas3.sign + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } ah_Meas0; + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } ah_Meas1; + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } ah_Meas2; + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } ah_Meas3; + u16 ah_CalSamples; + u32 ah_tx6PowerInHalfDbm; + u32 ah_staId1Defaults; + u32 ah_miscMode; + bool ah_tpcEnabled; + u32 ah_beaconInterval; + enum { + AUTO_32KHZ, + USE_32KHZ, + DONT_USE_32KHZ, + } ah_enable32kHzClock; + u32 *ah_analogBank0Data; + u32 *ah_analogBank1Data; + u32 *ah_analogBank2Data; + u32 *ah_analogBank3Data; + u32 *ah_analogBank6Data; + u32 *ah_analogBank6TPCData; + u32 *ah_analogBank7Data; + u32 *ah_addac5416_21; + u32 *ah_bank6Temp; + u32 ah_ofdmTxPower; + int16_t ah_txPowerIndexOffset; + u32 ah_slottime; + u32 ah_acktimeout; + u32 ah_ctstimeout; + u32 ah_globaltxtimeout; + u8 ah_gBeaconRate; + u32 ah_gpioSelect; + u32 ah_polarity; + u32 ah_gpioBit; + bool ah_eepEnabled; + u32 ah_procPhyErr; + bool ah_hasHwPhyCounters; + u32 ah_aniPeriod; + struct ar5416AniState *ah_curani; + struct ar5416AniState ah_ani[255]; + int ah_totalSizeDesired[5]; + int ah_coarseHigh[5]; + int ah_coarseLow[5]; + int ah_firpwr[5]; + u16 ah_ratesArray[16]; + u32 ah_intrTxqs; + bool ah_intrMitigation; + u32 ah_cycleCount; + u32 ah_ctlBusy; + u32 ah_extBusy; + enum ath9k_ht_extprotspacing ah_extprotspacing; + u8 ah_txchainmask; + u8 ah_rxchainmask; + int ah_hwp; + void __iomem *ah_cal_mem; + enum ath9k_ani_cmd ah_ani_function; + struct ar5416IniArray ah_iniModes; + struct ar5416IniArray ah_iniCommon; + struct ar5416IniArray ah_iniBank0; + struct ar5416IniArray ah_iniBB_RfGain; + struct ar5416IniArray ah_iniBank1; + struct ar5416IniArray ah_iniBank2; + struct ar5416IniArray ah_iniBank3; + struct ar5416IniArray ah_iniBank6; + struct ar5416IniArray ah_iniBank6TPC; + struct ar5416IniArray ah_iniBank7; + struct ar5416IniArray ah_iniAddac; + struct ar5416IniArray ah_iniPcieSerdes; + struct ar5416IniArray ah_iniModesAdditional; +}; +#define AH5416(_ah) ((struct ath_hal_5416 *)(_ah)) + +#define FREQ2FBIN(x, y) ((y) ? ((x) - 2300) : (((x) - 4800) / 5)) + +#define IS_5416_EMU(ah) \ + ((ah->ah_devid == AR5416_DEVID_EMU) || \ + (ah->ah_devid == AR5416_DEVID_EMU_PCIE)) + +#define ar5416RfDetach(ah) do { \ + if (AH5416(ah)->ah_rfHal.rfDetach != NULL) \ + AH5416(ah)->ah_rfHal.rfDetach(ah); \ + } while (0) + +#define ath9k_hw_use_flash(_ah) \ + (!(_ah->ah_flags & AH_USE_EEPROM)) + + +#define DO_DELAY(x) do { \ + if ((++(x) % 64) == 0) \ + udelay(1); \ + } while (0) + +#define REG_WRITE_ARRAY(iniarray, column, regWr) do { \ + int r; \ + for (r = 0; r < ((iniarray)->ia_rows); r++) { \ + REG_WRITE(ah, INI_RA((iniarray), (r), 0), \ + INI_RA((iniarray), r, (column))); \ + DO_DELAY(regWr); \ + } \ + } while (0) + +#define BASE_ACTIVATE_DELAY 100 +#define RTC_PLL_SETTLE_DELAY 1000 +#define COEF_SCALE_S 24 +#define HT40_CHANNEL_CENTER_SHIFT 10 + +#define ar5416CheckOpMode(_opmode) \ + ((_opmode == ATH9K_M_STA) || (_opmode == ATH9K_M_IBSS) || \ + (_opmode == ATH9K_M_HOSTAP) || (_opmode == ATH9K_M_MONITOR)) + +#define AR5416_EEPROM_MAGIC_OFFSET 0x0 + +#define AR5416_EEPROM_S 2 +#define AR5416_EEPROM_OFFSET 0x2000 +#define AR5416_EEPROM_START_ADDR \ + (AR_SREV_9100(ah)) ? 0x1fff1000 : 0x503f1200 +#define AR5416_EEPROM_MAX 0xae0 +#define ar5416_get_eep_ver(_ahp) \ + (((_ahp)->ah_eeprom.baseEepHeader.version >> 12) & 0xF) +#define ar5416_get_eep_rev(_ahp) \ + (((_ahp)->ah_eeprom.baseEepHeader.version) & 0xFFF) +#define ar5416_get_ntxchains(_txchainmask) \ + (((_txchainmask >> 2) & 1) + \ + ((_txchainmask >> 1) & 1) + (_txchainmask & 1)) + +#define IS_EEP_MINOR_V3(_ahp) \ + (ath9k_hw_get_eeprom((_ahp), EEP_MINOR_REV) >= AR5416_EEP_MINOR_VER_3) + +#define FIXED_CCA_THRESHOLD 15 + +#ifdef __BIG_ENDIAN +#define AR5416_EEPROM_MAGIC 0x5aa5 +#else +#define AR5416_EEPROM_MAGIC 0xa55a +#endif + +#define ATH9K_POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) + +#define ATH9K_ANTENNA0_CHAINMASK 0x1 +#define ATH9K_ANTENNA1_CHAINMASK 0x2 + +#define ATH9K_NUM_DMA_DEBUG_REGS 8 +#define ATH9K_NUM_QUEUES 10 + +#define HAL_NOISE_IMMUNE_MAX 4 +#define HAL_SPUR_IMMUNE_MAX 7 +#define HAL_FIRST_STEP_MAX 2 + +#define ATH9K_ANI_OFDM_TRIG_HIGH 500 +#define ATH9K_ANI_OFDM_TRIG_LOW 200 +#define ATH9K_ANI_CCK_TRIG_HIGH 200 +#define ATH9K_ANI_CCK_TRIG_LOW 100 +#define ATH9K_ANI_NOISE_IMMUNE_LVL 4 +#define ATH9K_ANI_USE_OFDM_WEAK_SIG true +#define ATH9K_ANI_CCK_WEAK_SIG_THR false +#define ATH9K_ANI_SPUR_IMMUNE_LVL 7 +#define ATH9K_ANI_FIRSTEP_LVL 0 +#define ATH9K_ANI_RSSI_THR_HIGH 40 +#define ATH9K_ANI_RSSI_THR_LOW 7 +#define ATH9K_ANI_PERIOD 100 + +#define AR_GPIOD_MASK 0x00001FFF +#define AR_GPIO_BIT(_gpio) (1 << (_gpio)) + +#define MAX_ANALOG_START 319 + +#define HAL_EP_RND(x, mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) +#define BEACON_RSSI(ahp) \ + HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \ + ATH9K_RSSI_EP_MULTIPLIER) + +#define ah_mibStats ah_stats.ast_mibstats + +#define AH_TIMEOUT 100000 +#define AH_TIME_QUANTUM 10 + +#define IS(_c, _f) (((_c)->channelFlags & _f) || 0) + +#define AR_KEYTABLE_SIZE 128 +#define POWER_UP_TIME 200000 + +#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) + +#define SUB_NUM_CTL_MODES_AT_5G_40 2 +#define SUB_NUM_CTL_MODES_AT_2G_40 3 +#define SPUR_RSSI_THRESH 40 + +#define TU_TO_USEC(_tu) ((_tu) << 10) + +#define CAB_TIMEOUT_VAL 10 +#define BEACON_TIMEOUT_VAL 10 +#define MIN_BEACON_TIMEOUT_VAL 1 +#define SLEEP_SLOP 3 + +#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_SIFS_TIME_HALF 32 +#define OFDM_PREAMBLE_TIME_HALF 40 +#define OFDM_PLCP_BITS_HALF 22 +#define OFDM_SYMBOL_TIME_HALF 8 + +#define OFDM_SIFS_TIME_QUARTER 64 +#define OFDM_PREAMBLE_TIME_QUARTER 80 +#define OFDM_PLCP_BITS_QUARTER 22 +#define OFDM_SYMBOL_TIME_QUARTER 16 + +u32 ath9k_hw_get_eeprom(struct ath_hal_5416 *ahp, + enum eeprom_param param); + +#endif diff --git a/drivers/net/wireless/ath9k/initvals.h b/drivers/net/wireless/ath9k/initvals.h new file mode 100644 index 0000000..3dd3815 --- /dev/null +++ b/drivers/net/wireless/ath9k/initvals.h @@ -0,0 +1,3146 @@ +/* + * 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. + */ + +static const u32 ar5416Modes_9100[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf }, + { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009844, 0x1372161e, 0x1372161e, 0x137216a0, 0x137216a0, 0x137216a0 }, + { 0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x00009850, 0x6de8b4e0, 0x6de8b4e0, 0x6de8b0de, 0x6de8b0de, 0x6de8b0de }, + { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e }, + { 0x00009860, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18 }, + { 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 }, + { 0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 }, + { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x000001b8, 0x00000370, 0x00000268, 0x00000134, 0x00000134 }, + { 0x00009924, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b }, + { 0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020 }, + { 0x00009960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 }, + { 0x0000a960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 }, + { 0x0000b960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 }, + { 0x00009964, 0x00000000, 0x00000000, 0x00001120, 0x00001120, 0x00001120 }, + { 0x0000c9bc, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00 }, + { 0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c }, + { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, + { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, + { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880, 0x00000880 }, + { 0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, 0xd03e4788 }, + { 0x0000a20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000b20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000c20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, 0x0a1a7caa }, + { 0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000 }, + { 0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, 0x2e032402 }, + { 0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, 0x4a0a3c06 }, + { 0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, 0x621a540b }, + { 0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, 0x764f6c1b }, + { 0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, 0x845b7a5a }, + { 0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, 0x950f8ccf }, + { 0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, 0xa5cf9b4f }, + { 0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, 0xbddfaf1f }, + { 0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, 0xd1ffc93f }, + { 0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, +}; + +static const u32 ar5416Common_9100[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020015 }, + { 0x00000034, 0x00000005 }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000008 }, + { 0x00000048, 0x00000008 }, + { 0x0000004c, 0x00000010 }, + { 0x00000050, 0x00000000 }, + { 0x00000054, 0x0000001f }, + { 0x00000800, 0x00000000 }, + { 0x00000804, 0x00000000 }, + { 0x00000808, 0x00000000 }, + { 0x0000080c, 0x00000000 }, + { 0x00000810, 0x00000000 }, + { 0x00000814, 0x00000000 }, + { 0x00000818, 0x00000000 }, + { 0x0000081c, 0x00000000 }, + { 0x00000820, 0x00000000 }, + { 0x00000824, 0x00000000 }, + { 0x00001040, 0x002ffc0f }, + { 0x00001044, 0x002ffc0f }, + { 0x00001048, 0x002ffc0f }, + { 0x0000104c, 0x002ffc0f }, + { 0x00001050, 0x002ffc0f }, + { 0x00001054, 0x002ffc0f }, + { 0x00001058, 0x002ffc0f }, + { 0x0000105c, 0x002ffc0f }, + { 0x00001060, 0x002ffc0f }, + { 0x00001064, 0x002ffc0f }, + { 0x00001230, 0x00000000 }, + { 0x00001270, 0x00000000 }, + { 0x00001038, 0x00000000 }, + { 0x00001078, 0x00000000 }, + { 0x000010b8, 0x00000000 }, + { 0x000010f8, 0x00000000 }, + { 0x00001138, 0x00000000 }, + { 0x00001178, 0x00000000 }, + { 0x000011b8, 0x00000000 }, + { 0x000011f8, 0x00000000 }, + { 0x00001238, 0x00000000 }, + { 0x00001278, 0x00000000 }, + { 0x000012b8, 0x00000000 }, + { 0x000012f8, 0x00000000 }, + { 0x00001338, 0x00000000 }, + { 0x00001378, 0x00000000 }, + { 0x000013b8, 0x00000000 }, + { 0x000013f8, 0x00000000 }, + { 0x00001438, 0x00000000 }, + { 0x00001478, 0x00000000 }, + { 0x000014b8, 0x00000000 }, + { 0x000014f8, 0x00000000 }, + { 0x00001538, 0x00000000 }, + { 0x00001578, 0x00000000 }, + { 0x000015b8, 0x00000000 }, + { 0x000015f8, 0x00000000 }, + { 0x00001638, 0x00000000 }, + { 0x00001678, 0x00000000 }, + { 0x000016b8, 0x00000000 }, + { 0x000016f8, 0x00000000 }, + { 0x00001738, 0x00000000 }, + { 0x00001778, 0x00000000 }, + { 0x000017b8, 0x00000000 }, + { 0x000017f8, 0x00000000 }, + { 0x0000103c, 0x00000000 }, + { 0x0000107c, 0x00000000 }, + { 0x000010bc, 0x00000000 }, + { 0x000010fc, 0x00000000 }, + { 0x0000113c, 0x00000000 }, + { 0x0000117c, 0x00000000 }, + { 0x000011bc, 0x00000000 }, + { 0x000011fc, 0x00000000 }, + { 0x0000123c, 0x00000000 }, + { 0x0000127c, 0x00000000 }, + { 0x000012bc, 0x00000000 }, + { 0x000012fc, 0x00000000 }, + { 0x0000133c, 0x00000000 }, + { 0x0000137c, 0x00000000 }, + { 0x000013bc, 0x00000000 }, + { 0x000013fc, 0x00000000 }, + { 0x0000143c, 0x00000000 }, + { 0x0000147c, 0x00000000 }, + { 0x00004030, 0x00000002 }, + { 0x0000403c, 0x00000002 }, + { 0x00007010, 0x00000000 }, + { 0x00007038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x40000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x000080c0, 0x2a82301a }, + { 0x000080c4, 0x05dc01e0 }, + { 0x000080c8, 0x1f402710 }, + { 0x000080cc, 0x01f40000 }, + { 0x000080d0, 0x00001e00 }, + { 0x000080d4, 0x00000000 }, + { 0x000080d8, 0x00400000 }, + { 0x000080e0, 0xffffffff }, + { 0x000080e4, 0x0000ffff }, + { 0x000080e8, 0x003f3f3f }, + { 0x000080ec, 0x00000000 }, + { 0x000080f0, 0x00000000 }, + { 0x000080f4, 0x00000000 }, + { 0x000080f8, 0x00000000 }, + { 0x000080fc, 0x00020000 }, + { 0x00008100, 0x00020000 }, + { 0x00008104, 0x00000001 }, + { 0x00008108, 0x00000052 }, + { 0x0000810c, 0x00000000 }, + { 0x00008110, 0x00000168 }, + { 0x00008118, 0x000100aa }, + { 0x0000811c, 0x00003210 }, + { 0x00008120, 0x08f04800 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0x00000000 }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c4, 0x00000000 }, + { 0x000081d0, 0x00003210 }, + { 0x000081ec, 0x00000000 }, + { 0x000081f0, 0x00000000 }, + { 0x000081f4, 0x00000000 }, + { 0x000081f8, 0x00000000 }, + { 0x000081fc, 0x00000000 }, + { 0x00008200, 0x00000000 }, + { 0x00008204, 0x00000000 }, + { 0x00008208, 0x00000000 }, + { 0x0000820c, 0x00000000 }, + { 0x00008210, 0x00000000 }, + { 0x00008214, 0x00000000 }, + { 0x00008218, 0x00000000 }, + { 0x0000821c, 0x00000000 }, + { 0x00008220, 0x00000000 }, + { 0x00008224, 0x00000000 }, + { 0x00008228, 0x00000000 }, + { 0x0000822c, 0x00000000 }, + { 0x00008230, 0x00000000 }, + { 0x00008234, 0x00000000 }, + { 0x00008238, 0x00000000 }, + { 0x0000823c, 0x00000000 }, + { 0x00008240, 0x00100000 }, + { 0x00008244, 0x0010f400 }, + { 0x00008248, 0x00000100 }, + { 0x0000824c, 0x0001e800 }, + { 0x00008250, 0x00000000 }, + { 0x00008254, 0x00000000 }, + { 0x00008258, 0x00000000 }, + { 0x0000825c, 0x400000ff }, + { 0x00008260, 0x00080922 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x00008300, 0x00000000 }, + { 0x00008304, 0x00000000 }, + { 0x00008308, 0x00000000 }, + { 0x0000830c, 0x00000000 }, + { 0x00008310, 0x00000000 }, + { 0x00008314, 0x00000000 }, + { 0x00008318, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00000000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xad848e19 }, + { 0x00009810, 0x7d14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x00009840, 0x206a002e }, + { 0x0000984c, 0x1284233c }, + { 0x00009854, 0x00000859 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x05100000 }, + { 0x0000a920, 0x05100000 }, + { 0x0000b920, 0x05100000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280b212 }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5d50e188 }, + { 0x00009958, 0x00081fff }, + { 0x0000c95c, 0x004b6a8e }, + { 0x0000c968, 0x000003ce }, + { 0x00009970, 0x190fb515 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x00009980, 0x00000000 }, + { 0x00009984, 0x00000000 }, + { 0x00009988, 0x00000000 }, + { 0x0000998c, 0x00000000 }, + { 0x00009990, 0x00000000 }, + { 0x00009994, 0x00000000 }, + { 0x00009998, 0x00000000 }, + { 0x0000999c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x001fff00 }, + { 0x000099ac, 0x00000000 }, + { 0x000099b0, 0x03051000 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000200 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x000000aa }, + { 0x000099fc, 0x00001042 }, + { 0x00009b00, 0x00000000 }, + { 0x00009b04, 0x00000001 }, + { 0x00009b08, 0x00000002 }, + { 0x00009b0c, 0x00000003 }, + { 0x00009b10, 0x00000004 }, + { 0x00009b14, 0x00000005 }, + { 0x00009b18, 0x00000008 }, + { 0x00009b1c, 0x00000009 }, + { 0x00009b20, 0x0000000a }, + { 0x00009b24, 0x0000000b }, + { 0x00009b28, 0x0000000c }, + { 0x00009b2c, 0x0000000d }, + { 0x00009b30, 0x00000010 }, + { 0x00009b34, 0x00000011 }, + { 0x00009b38, 0x00000012 }, + { 0x00009b3c, 0x00000013 }, + { 0x00009b40, 0x00000014 }, + { 0x00009b44, 0x00000015 }, + { 0x00009b48, 0x00000018 }, + { 0x00009b4c, 0x00000019 }, + { 0x00009b50, 0x0000001a }, + { 0x00009b54, 0x0000001b }, + { 0x00009b58, 0x0000001c }, + { 0x00009b5c, 0x0000001d }, + { 0x00009b60, 0x00000020 }, + { 0x00009b64, 0x00000021 }, + { 0x00009b68, 0x00000022 }, + { 0x00009b6c, 0x00000023 }, + { 0x00009b70, 0x00000024 }, + { 0x00009b74, 0x00000025 }, + { 0x00009b78, 0x00000028 }, + { 0x00009b7c, 0x00000029 }, + { 0x00009b80, 0x0000002a }, + { 0x00009b84, 0x0000002b }, + { 0x00009b88, 0x0000002c }, + { 0x00009b8c, 0x0000002d }, + { 0x00009b90, 0x00000030 }, + { 0x00009b94, 0x00000031 }, + { 0x00009b98, 0x00000032 }, + { 0x00009b9c, 0x00000033 }, + { 0x00009ba0, 0x00000034 }, + { 0x00009ba4, 0x00000035 }, + { 0x00009ba8, 0x00000035 }, + { 0x00009bac, 0x00000035 }, + { 0x00009bb0, 0x00000035 }, + { 0x00009bb4, 0x00000035 }, + { 0x00009bb8, 0x00000035 }, + { 0x00009bbc, 0x00000035 }, + { 0x00009bc0, 0x00000035 }, + { 0x00009bc4, 0x00000035 }, + { 0x00009bc8, 0x00000035 }, + { 0x00009bcc, 0x00000035 }, + { 0x00009bd0, 0x00000035 }, + { 0x00009bd4, 0x00000035 }, + { 0x00009bd8, 0x00000035 }, + { 0x00009bdc, 0x00000035 }, + { 0x00009be0, 0x00000035 }, + { 0x00009be4, 0x00000035 }, + { 0x00009be8, 0x00000035 }, + { 0x00009bec, 0x00000035 }, + { 0x00009bf0, 0x00000035 }, + { 0x00009bf4, 0x00000035 }, + { 0x00009bf8, 0x00000010 }, + { 0x00009bfc, 0x0000001a }, + { 0x0000a210, 0x40806333 }, + { 0x0000a214, 0x00106c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x018830c6 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x00000bb5 }, + { 0x0000a22c, 0x00000011 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c889af }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00007bb6 }, + { 0x0000a248, 0x0fff3ffc }, + { 0x0000a24c, 0x00000001 }, + { 0x0000a250, 0x0000a000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cc75380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0ebae9c6 }, + { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000c26c, 0x0ebae9c6 }, + { 0x0000d270, 0x00820820 }, + { 0x0000a278, 0x1ce739ce }, + { 0x0000a27c, 0x051701ce }, + { 0x0000a338, 0x00000000 }, + { 0x0000a33c, 0x00000000 }, + { 0x0000a340, 0x00000000 }, + { 0x0000a344, 0x00000000 }, + { 0x0000a348, 0x3fffffff }, + { 0x0000a34c, 0x3fffffff }, + { 0x0000a350, 0x3fffffff }, + { 0x0000a354, 0x0003ffff }, + { 0x0000a358, 0x79a8aa1f }, + { 0x0000d35c, 0x07ffffef }, + { 0x0000d360, 0x0fffffe7 }, + { 0x0000d364, 0x17ffffe5 }, + { 0x0000d368, 0x1fffffe4 }, + { 0x0000d36c, 0x37ffffe3 }, + { 0x0000d370, 0x3fffffe3 }, + { 0x0000d374, 0x57ffffe3 }, + { 0x0000d378, 0x5fffffe2 }, + { 0x0000d37c, 0x7fffffe2 }, + { 0x0000d380, 0x7f3c7bba }, + { 0x0000d384, 0xf3307ff0 }, + { 0x0000a388, 0x08000000 }, + { 0x0000a38c, 0x20202020 }, + { 0x0000a390, 0x20202020 }, + { 0x0000a394, 0x1ce739ce }, + { 0x0000a398, 0x000001ce }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3c8, 0x00000246 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce }, +}; + +static const u32 ar5416Bank0_9100[][2] = { + { 0x000098b0, 0x1e5795e5 }, + { 0x000098e0, 0x02008020 }, +}; + +static const u32 ar5416BB_RfGain_9100[][3] = { + { 0x00009a00, 0x00000000, 0x00000000 }, + { 0x00009a04, 0x00000040, 0x00000040 }, + { 0x00009a08, 0x00000080, 0x00000080 }, + { 0x00009a0c, 0x000001a1, 0x00000141 }, + { 0x00009a10, 0x000001e1, 0x00000181 }, + { 0x00009a14, 0x00000021, 0x000001c1 }, + { 0x00009a18, 0x00000061, 0x00000001 }, + { 0x00009a1c, 0x00000168, 0x00000041 }, + { 0x00009a20, 0x000001a8, 0x000001a8 }, + { 0x00009a24, 0x000001e8, 0x000001e8 }, + { 0x00009a28, 0x00000028, 0x00000028 }, + { 0x00009a2c, 0x00000068, 0x00000068 }, + { 0x00009a30, 0x00000189, 0x000000a8 }, + { 0x00009a34, 0x000001c9, 0x00000169 }, + { 0x00009a38, 0x00000009, 0x000001a9 }, + { 0x00009a3c, 0x00000049, 0x000001e9 }, + { 0x00009a40, 0x00000089, 0x00000029 }, + { 0x00009a44, 0x00000170, 0x00000069 }, + { 0x00009a48, 0x000001b0, 0x00000190 }, + { 0x00009a4c, 0x000001f0, 0x000001d0 }, + { 0x00009a50, 0x00000030, 0x00000010 }, + { 0x00009a54, 0x00000070, 0x00000050 }, + { 0x00009a58, 0x00000191, 0x00000090 }, + { 0x00009a5c, 0x000001d1, 0x00000151 }, + { 0x00009a60, 0x00000011, 0x00000191 }, + { 0x00009a64, 0x00000051, 0x000001d1 }, + { 0x00009a68, 0x00000091, 0x00000011 }, + { 0x00009a6c, 0x000001b8, 0x00000051 }, + { 0x00009a70, 0x000001f8, 0x00000198 }, + { 0x00009a74, 0x00000038, 0x000001d8 }, + { 0x00009a78, 0x00000078, 0x00000018 }, + { 0x00009a7c, 0x00000199, 0x00000058 }, + { 0x00009a80, 0x000001d9, 0x00000098 }, + { 0x00009a84, 0x00000019, 0x00000159 }, + { 0x00009a88, 0x00000059, 0x00000199 }, + { 0x00009a8c, 0x00000099, 0x000001d9 }, + { 0x00009a90, 0x000000d9, 0x00000019 }, + { 0x00009a94, 0x000000f9, 0x00000059 }, + { 0x00009a98, 0x000000f9, 0x00000099 }, + { 0x00009a9c, 0x000000f9, 0x000000d9 }, + { 0x00009aa0, 0x000000f9, 0x000000f9 }, + { 0x00009aa4, 0x000000f9, 0x000000f9 }, + { 0x00009aa8, 0x000000f9, 0x000000f9 }, + { 0x00009aac, 0x000000f9, 0x000000f9 }, + { 0x00009ab0, 0x000000f9, 0x000000f9 }, + { 0x00009ab4, 0x000000f9, 0x000000f9 }, + { 0x00009ab8, 0x000000f9, 0x000000f9 }, + { 0x00009abc, 0x000000f9, 0x000000f9 }, + { 0x00009ac0, 0x000000f9, 0x000000f9 }, + { 0x00009ac4, 0x000000f9, 0x000000f9 }, + { 0x00009ac8, 0x000000f9, 0x000000f9 }, + { 0x00009acc, 0x000000f9, 0x000000f9 }, + { 0x00009ad0, 0x000000f9, 0x000000f9 }, + { 0x00009ad4, 0x000000f9, 0x000000f9 }, + { 0x00009ad8, 0x000000f9, 0x000000f9 }, + { 0x00009adc, 0x000000f9, 0x000000f9 }, + { 0x00009ae0, 0x000000f9, 0x000000f9 }, + { 0x00009ae4, 0x000000f9, 0x000000f9 }, + { 0x00009ae8, 0x000000f9, 0x000000f9 }, + { 0x00009aec, 0x000000f9, 0x000000f9 }, + { 0x00009af0, 0x000000f9, 0x000000f9 }, + { 0x00009af4, 0x000000f9, 0x000000f9 }, + { 0x00009af8, 0x000000f9, 0x000000f9 }, + { 0x00009afc, 0x000000f9, 0x000000f9 }, +}; + +static const u32 ar5416Bank1_9100[][2] = { + { 0x000098b0, 0x02108421 }, + { 0x000098ec, 0x00000008 }, +}; + +static const u32 ar5416Bank2_9100[][2] = { + { 0x000098b0, 0x0e73ff17 }, + { 0x000098e0, 0x00000420 }, +}; + +static const u32 ar5416Bank3_9100[][3] = { + { 0x000098f0, 0x01400018, 0x01c00018 }, +}; + +static const u32 ar5416Bank6_9100[][3] = { + + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x40ff0000, 0x40ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x004210a2, 0x004210a2 }, + { 0x0000989c, 0x0014008f, 0x0014008f }, + { 0x0000989c, 0x00c40003, 0x00c40003 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x0001805e, 0x0001805e }, + { 0x0000989c, 0x0000c0ab, 0x0000c0ab }, + { 0x0000989c, 0x000000f1, 0x000000f1 }, + { 0x0000989c, 0x00002081, 0x00002081 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + +static const u32 ar5416Bank6TPC_9100[][3] = { + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x40ff0000, 0x40ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x00423022, 0x00423022 }, + { 0x0000989c, 0x201400df, 0x201400df }, + { 0x0000989c, 0x00c40002, 0x00c40002 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x0001805e, 0x0001805e }, + { 0x0000989c, 0x0000c0ab, 0x0000c0ab }, + { 0x0000989c, 0x000000e1, 0x000000e1 }, + { 0x0000989c, 0x00007081, 0x00007081 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + +static const u32 ar5416Bank7_9100[][2] = { + { 0x0000989c, 0x00000500 }, + { 0x0000989c, 0x00000800 }, + { 0x000098cc, 0x0000000e }, +}; + +static const u32 ar5416Addac_9100[][2] = { + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000003 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x0000000c }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000030 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000060 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000058 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x000098c4, 0x00000000 }, +}; + +static const u32 ar5416Modes[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf }, + { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0 }, + { 0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x00009850, 0x6d48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6d48b0e2, 0x6d48b0e2 }, + { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec86d2e, 0x7ec84d2e, 0x7ec82d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e }, + { 0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18 }, + { 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0 }, + { 0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 }, + { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a11, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009940, 0x00754604, 0x00754604, 0xfff81204, 0xfff81204, 0xfff81204 }, + { 0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020 }, + { 0x00009954, 0x5f3ca3de, 0x5f3ca3de, 0xe250a51e, 0xe250a51e, 0xe250a51e }, + { 0x00009958, 0x2108ecff, 0x2108ecff, 0x3388ffff, 0x3388ffff, 0x3388ffff }, +#ifdef TB243 + { 0x00009960, 0x00000900, 0x00000900, 0x00009b40, 0x00009b40, 0x00012d80 }, + { 0x0000a960, 0x00000900, 0x00000900, 0x00009b40, 0x00009b40, 0x00012d80 }, + { 0x0000b960, 0x00000900, 0x00000900, 0x00009b40, 0x00009b40, 0x00012d80 }, + { 0x00009964, 0x00000000, 0x00000000, 0x00002210, 0x00002210, 0x00001120 }, +#else + { 0x00009960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0 }, + { 0x0000a960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0 }, + { 0x0000b960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0 }, + { 0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, 0x00001120 }, +#endif + { 0x0000c9bc, 0x001a0600, 0x001a0600, 0x001a1000, 0x001a0c00, 0x001a0c00 }, + { 0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329 }, + { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, + { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, + { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880, 0x00000880 }, + { 0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, 0xd03e4788 }, + { 0x0000a20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000b20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000c20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, 0x0a1a7caa }, + { 0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000 }, + { 0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, 0x2e032402 }, + { 0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, 0x4a0a3c06 }, + { 0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, 0x621a540b }, + { 0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, 0x764f6c1b }, + { 0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, 0x845b7a5a }, + { 0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, 0x950f8ccf }, + { 0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, 0xa5cf9b4f }, + { 0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, 0xbddfaf1f }, + { 0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, 0xd1ffc93f }, + { 0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, +}; + +static const u32 ar5416Common[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020015 }, + { 0x00000034, 0x00000005 }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000008 }, + { 0x00000048, 0x00000008 }, + { 0x0000004c, 0x00000010 }, + { 0x00000050, 0x00000000 }, + { 0x00000054, 0x0000001f }, + { 0x00000800, 0x00000000 }, + { 0x00000804, 0x00000000 }, + { 0x00000808, 0x00000000 }, + { 0x0000080c, 0x00000000 }, + { 0x00000810, 0x00000000 }, + { 0x00000814, 0x00000000 }, + { 0x00000818, 0x00000000 }, + { 0x0000081c, 0x00000000 }, + { 0x00000820, 0x00000000 }, + { 0x00000824, 0x00000000 }, + { 0x00001040, 0x002ffc0f }, + { 0x00001044, 0x002ffc0f }, + { 0x00001048, 0x002ffc0f }, + { 0x0000104c, 0x002ffc0f }, + { 0x00001050, 0x002ffc0f }, + { 0x00001054, 0x002ffc0f }, + { 0x00001058, 0x002ffc0f }, + { 0x0000105c, 0x002ffc0f }, + { 0x00001060, 0x002ffc0f }, + { 0x00001064, 0x002ffc0f }, + { 0x00001230, 0x00000000 }, + { 0x00001270, 0x00000000 }, + { 0x00001038, 0x00000000 }, + { 0x00001078, 0x00000000 }, + { 0x000010b8, 0x00000000 }, + { 0x000010f8, 0x00000000 }, + { 0x00001138, 0x00000000 }, + { 0x00001178, 0x00000000 }, + { 0x000011b8, 0x00000000 }, + { 0x000011f8, 0x00000000 }, + { 0x00001238, 0x00000000 }, + { 0x00001278, 0x00000000 }, + { 0x000012b8, 0x00000000 }, + { 0x000012f8, 0x00000000 }, + { 0x00001338, 0x00000000 }, + { 0x00001378, 0x00000000 }, + { 0x000013b8, 0x00000000 }, + { 0x000013f8, 0x00000000 }, + { 0x00001438, 0x00000000 }, + { 0x00001478, 0x00000000 }, + { 0x000014b8, 0x00000000 }, + { 0x000014f8, 0x00000000 }, + { 0x00001538, 0x00000000 }, + { 0x00001578, 0x00000000 }, + { 0x000015b8, 0x00000000 }, + { 0x000015f8, 0x00000000 }, + { 0x00001638, 0x00000000 }, + { 0x00001678, 0x00000000 }, + { 0x000016b8, 0x00000000 }, + { 0x000016f8, 0x00000000 }, + { 0x00001738, 0x00000000 }, + { 0x00001778, 0x00000000 }, + { 0x000017b8, 0x00000000 }, + { 0x000017f8, 0x00000000 }, + { 0x0000103c, 0x00000000 }, + { 0x0000107c, 0x00000000 }, + { 0x000010bc, 0x00000000 }, + { 0x000010fc, 0x00000000 }, + { 0x0000113c, 0x00000000 }, + { 0x0000117c, 0x00000000 }, + { 0x000011bc, 0x00000000 }, + { 0x000011fc, 0x00000000 }, + { 0x0000123c, 0x00000000 }, + { 0x0000127c, 0x00000000 }, + { 0x000012bc, 0x00000000 }, + { 0x000012fc, 0x00000000 }, + { 0x0000133c, 0x00000000 }, + { 0x0000137c, 0x00000000 }, + { 0x000013bc, 0x00000000 }, + { 0x000013fc, 0x00000000 }, + { 0x0000143c, 0x00000000 }, + { 0x0000147c, 0x00000000 }, + { 0x00020010, 0x00000003 }, + { 0x00020038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x40000000 }, + { 0x00008054, 0x00004000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x000080c0, 0x2a82301a }, + { 0x000080c4, 0x05dc01e0 }, + { 0x000080c8, 0x1f402710 }, + { 0x000080cc, 0x01f40000 }, + { 0x000080d0, 0x00001e00 }, + { 0x000080d4, 0x00000000 }, + { 0x000080d8, 0x00400000 }, + { 0x000080e0, 0xffffffff }, + { 0x000080e4, 0x0000ffff }, + { 0x000080e8, 0x003f3f3f }, + { 0x000080ec, 0x00000000 }, + { 0x000080f0, 0x00000000 }, + { 0x000080f4, 0x00000000 }, + { 0x000080f8, 0x00000000 }, + { 0x000080fc, 0x00020000 }, + { 0x00008100, 0x00020000 }, + { 0x00008104, 0x00000001 }, + { 0x00008108, 0x00000052 }, + { 0x0000810c, 0x00000000 }, + { 0x00008110, 0x00000168 }, + { 0x00008118, 0x000100aa }, + { 0x0000811c, 0x00003210 }, + { 0x00008120, 0x08f04800 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0x00000000 }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c4, 0x00000000 }, + { 0x000081d0, 0x00003210 }, + { 0x000081ec, 0x00000000 }, + { 0x000081f0, 0x00000000 }, + { 0x000081f4, 0x00000000 }, + { 0x000081f8, 0x00000000 }, + { 0x000081fc, 0x00000000 }, + { 0x00008200, 0x00000000 }, + { 0x00008204, 0x00000000 }, + { 0x00008208, 0x00000000 }, + { 0x0000820c, 0x00000000 }, + { 0x00008210, 0x00000000 }, + { 0x00008214, 0x00000000 }, + { 0x00008218, 0x00000000 }, + { 0x0000821c, 0x00000000 }, + { 0x00008220, 0x00000000 }, + { 0x00008224, 0x00000000 }, + { 0x00008228, 0x00000000 }, + { 0x0000822c, 0x00000000 }, + { 0x00008230, 0x00000000 }, + { 0x00008234, 0x00000000 }, + { 0x00008238, 0x00000000 }, + { 0x0000823c, 0x00000000 }, + { 0x00008240, 0x00100000 }, + { 0x00008244, 0x0010f400 }, + { 0x00008248, 0x00000100 }, + { 0x0000824c, 0x0001e800 }, + { 0x00008250, 0x00000000 }, + { 0x00008254, 0x00000000 }, + { 0x00008258, 0x00000000 }, + { 0x0000825c, 0x400000ff }, + { 0x00008260, 0x00080922 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x00008300, 0x00000000 }, + { 0x00008304, 0x00000000 }, + { 0x00008308, 0x00000000 }, + { 0x0000830c, 0x00000000 }, + { 0x00008310, 0x00000000 }, + { 0x00008314, 0x00000000 }, + { 0x00008318, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00000000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xad848e19 }, + { 0x00009810, 0x7d14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x00009840, 0x206a01ae }, + { 0x0000984c, 0x1284233c }, + { 0x00009854, 0x00000859 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x05100000 }, + { 0x0000a920, 0x05100000 }, + { 0x0000b920, 0x05100000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280b212 }, + { 0x0000994c, 0x00020028 }, + { 0x0000c95c, 0x004b6a8e }, + { 0x0000c968, 0x000003ce }, + { 0x00009970, 0x190fb514 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x00009980, 0x00000000 }, + { 0x00009984, 0x00000000 }, + { 0x00009988, 0x00000000 }, + { 0x0000998c, 0x00000000 }, + { 0x00009990, 0x00000000 }, + { 0x00009994, 0x00000000 }, + { 0x00009998, 0x00000000 }, + { 0x0000999c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x201fff00 }, + { 0x000099ac, 0x006f0000 }, + { 0x000099b0, 0x03051000 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000200 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099fc, 0x00001042 }, + { 0x00009b00, 0x00000000 }, + { 0x00009b04, 0x00000001 }, + { 0x00009b08, 0x00000002 }, + { 0x00009b0c, 0x00000003 }, + { 0x00009b10, 0x00000004 }, + { 0x00009b14, 0x00000005 }, + { 0x00009b18, 0x00000008 }, + { 0x00009b1c, 0x00000009 }, + { 0x00009b20, 0x0000000a }, + { 0x00009b24, 0x0000000b }, + { 0x00009b28, 0x0000000c }, + { 0x00009b2c, 0x0000000d }, + { 0x00009b30, 0x00000010 }, + { 0x00009b34, 0x00000011 }, + { 0x00009b38, 0x00000012 }, + { 0x00009b3c, 0x00000013 }, + { 0x00009b40, 0x00000014 }, + { 0x00009b44, 0x00000015 }, + { 0x00009b48, 0x00000018 }, + { 0x00009b4c, 0x00000019 }, + { 0x00009b50, 0x0000001a }, + { 0x00009b54, 0x0000001b }, + { 0x00009b58, 0x0000001c }, + { 0x00009b5c, 0x0000001d }, + { 0x00009b60, 0x00000020 }, + { 0x00009b64, 0x00000021 }, + { 0x00009b68, 0x00000022 }, + { 0x00009b6c, 0x00000023 }, + { 0x00009b70, 0x00000024 }, + { 0x00009b74, 0x00000025 }, + { 0x00009b78, 0x00000028 }, + { 0x00009b7c, 0x00000029 }, + { 0x00009b80, 0x0000002a }, + { 0x00009b84, 0x0000002b }, + { 0x00009b88, 0x0000002c }, + { 0x00009b8c, 0x0000002d }, + { 0x00009b90, 0x00000030 }, + { 0x00009b94, 0x00000031 }, + { 0x00009b98, 0x00000032 }, + { 0x00009b9c, 0x00000033 }, + { 0x00009ba0, 0x00000034 }, + { 0x00009ba4, 0x00000035 }, + { 0x00009ba8, 0x00000035 }, + { 0x00009bac, 0x00000035 }, + { 0x00009bb0, 0x00000035 }, + { 0x00009bb4, 0x00000035 }, + { 0x00009bb8, 0x00000035 }, + { 0x00009bbc, 0x00000035 }, + { 0x00009bc0, 0x00000035 }, + { 0x00009bc4, 0x00000035 }, + { 0x00009bc8, 0x00000035 }, + { 0x00009bcc, 0x00000035 }, + { 0x00009bd0, 0x00000035 }, + { 0x00009bd4, 0x00000035 }, + { 0x00009bd8, 0x00000035 }, + { 0x00009bdc, 0x00000035 }, + { 0x00009be0, 0x00000035 }, + { 0x00009be4, 0x00000035 }, + { 0x00009be8, 0x00000035 }, + { 0x00009bec, 0x00000035 }, + { 0x00009bf0, 0x00000035 }, + { 0x00009bf4, 0x00000035 }, + { 0x00009bf8, 0x00000010 }, + { 0x00009bfc, 0x0000001a }, + { 0x0000a210, 0x40806333 }, + { 0x0000a214, 0x00106c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x018830c6 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x001a0bb5 }, + { 0x0000a22c, 0x00000000 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c889ae }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00007bb6 }, + { 0x0000a248, 0x0fff3ffc }, + { 0x0000a24c, 0x00000001 }, + { 0x0000a250, 0x0000a000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cc75380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a268, 0x00000001 }, + { 0x0000a26c, 0x0ebae9c6 }, + { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000c26c, 0x0ebae9c6 }, + { 0x0000d270, 0x00820820 }, + { 0x0000a278, 0x1ce739ce }, + { 0x0000a27c, 0x050701ce }, + { 0x0000a338, 0x00000000 }, + { 0x0000a33c, 0x00000000 }, + { 0x0000a340, 0x00000000 }, + { 0x0000a344, 0x00000000 }, + { 0x0000a348, 0x3fffffff }, + { 0x0000a34c, 0x3fffffff }, + { 0x0000a350, 0x3fffffff }, + { 0x0000a354, 0x0003ffff }, + { 0x0000a358, 0x79a8aa33 }, + { 0x0000d35c, 0x07ffffef }, + { 0x0000d360, 0x0fffffe7 }, + { 0x0000d364, 0x17ffffe5 }, + { 0x0000d368, 0x1fffffe4 }, + { 0x0000d36c, 0x37ffffe3 }, + { 0x0000d370, 0x3fffffe3 }, + { 0x0000d374, 0x57ffffe3 }, + { 0x0000d378, 0x5fffffe2 }, + { 0x0000d37c, 0x7fffffe2 }, + { 0x0000d380, 0x7f3c7bba }, + { 0x0000d384, 0xf3307ff0 }, + { 0x0000a388, 0x0c000000 }, + { 0x0000a38c, 0x20202020 }, + { 0x0000a390, 0x20202020 }, + { 0x0000a394, 0x1ce739ce }, + { 0x0000a398, 0x000001ce }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3c8, 0x00000246 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce }, +}; + +static const u32 ar5416Bank0[][2] = { + { 0x000098b0, 0x1e5795e5 }, + { 0x000098e0, 0x02008020 }, +}; + +static const u32 ar5416BB_RfGain[][3] = { + { 0x00009a00, 0x00000000, 0x00000000 }, + { 0x00009a04, 0x00000040, 0x00000040 }, + { 0x00009a08, 0x00000080, 0x00000080 }, + { 0x00009a0c, 0x000001a1, 0x00000141 }, + { 0x00009a10, 0x000001e1, 0x00000181 }, + { 0x00009a14, 0x00000021, 0x000001c1 }, + { 0x00009a18, 0x00000061, 0x00000001 }, + { 0x00009a1c, 0x00000168, 0x00000041 }, + { 0x00009a20, 0x000001a8, 0x000001a8 }, + { 0x00009a24, 0x000001e8, 0x000001e8 }, + { 0x00009a28, 0x00000028, 0x00000028 }, + { 0x00009a2c, 0x00000068, 0x00000068 }, + { 0x00009a30, 0x00000189, 0x000000a8 }, + { 0x00009a34, 0x000001c9, 0x00000169 }, + { 0x00009a38, 0x00000009, 0x000001a9 }, + { 0x00009a3c, 0x00000049, 0x000001e9 }, + { 0x00009a40, 0x00000089, 0x00000029 }, + { 0x00009a44, 0x00000170, 0x00000069 }, + { 0x00009a48, 0x000001b0, 0x00000190 }, + { 0x00009a4c, 0x000001f0, 0x000001d0 }, + { 0x00009a50, 0x00000030, 0x00000010 }, + { 0x00009a54, 0x00000070, 0x00000050 }, + { 0x00009a58, 0x00000191, 0x00000090 }, + { 0x00009a5c, 0x000001d1, 0x00000151 }, + { 0x00009a60, 0x00000011, 0x00000191 }, + { 0x00009a64, 0x00000051, 0x000001d1 }, + { 0x00009a68, 0x00000091, 0x00000011 }, + { 0x00009a6c, 0x000001b8, 0x00000051 }, + { 0x00009a70, 0x000001f8, 0x00000198 }, + { 0x00009a74, 0x00000038, 0x000001d8 }, + { 0x00009a78, 0x00000078, 0x00000018 }, + { 0x00009a7c, 0x00000199, 0x00000058 }, + { 0x00009a80, 0x000001d9, 0x00000098 }, + { 0x00009a84, 0x00000019, 0x00000159 }, + { 0x00009a88, 0x00000059, 0x00000199 }, + { 0x00009a8c, 0x00000099, 0x000001d9 }, + { 0x00009a90, 0x000000d9, 0x00000019 }, + { 0x00009a94, 0x000000f9, 0x00000059 }, + { 0x00009a98, 0x000000f9, 0x00000099 }, + { 0x00009a9c, 0x000000f9, 0x000000d9 }, + { 0x00009aa0, 0x000000f9, 0x000000f9 }, + { 0x00009aa4, 0x000000f9, 0x000000f9 }, + { 0x00009aa8, 0x000000f9, 0x000000f9 }, + { 0x00009aac, 0x000000f9, 0x000000f9 }, + { 0x00009ab0, 0x000000f9, 0x000000f9 }, + { 0x00009ab4, 0x000000f9, 0x000000f9 }, + { 0x00009ab8, 0x000000f9, 0x000000f9 }, + { 0x00009abc, 0x000000f9, 0x000000f9 }, + { 0x00009ac0, 0x000000f9, 0x000000f9 }, + { 0x00009ac4, 0x000000f9, 0x000000f9 }, + { 0x00009ac8, 0x000000f9, 0x000000f9 }, + { 0x00009acc, 0x000000f9, 0x000000f9 }, + { 0x00009ad0, 0x000000f9, 0x000000f9 }, + { 0x00009ad4, 0x000000f9, 0x000000f9 }, + { 0x00009ad8, 0x000000f9, 0x000000f9 }, + { 0x00009adc, 0x000000f9, 0x000000f9 }, + { 0x00009ae0, 0x000000f9, 0x000000f9 }, + { 0x00009ae4, 0x000000f9, 0x000000f9 }, + { 0x00009ae8, 0x000000f9, 0x000000f9 }, + { 0x00009aec, 0x000000f9, 0x000000f9 }, + { 0x00009af0, 0x000000f9, 0x000000f9 }, + { 0x00009af4, 0x000000f9, 0x000000f9 }, + { 0x00009af8, 0x000000f9, 0x000000f9 }, + { 0x00009afc, 0x000000f9, 0x000000f9 }, +}; + +static const u32 ar5416Bank1[][2] = { + { 0x000098b0, 0x02108421}, + { 0x000098ec, 0x00000008}, +}; + +static const u32 ar5416Bank2[][2] = { + { 0x000098b0, 0x0e73ff17}, + { 0x000098e0, 0x00000420}, +}; + +static const u32 ar5416Bank3[][3] = { + { 0x000098f0, 0x01400018, 0x01c00018 }, +}; + +static const u32 ar5416Bank6[][3] = { + + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x004210a2, 0x004210a2 }, + { 0x0000989c, 0x0014000f, 0x0014000f }, + { 0x0000989c, 0x00c40002, 0x00c40002 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x000180d6, 0x000180d6 }, + { 0x0000989c, 0x0000c0aa, 0x0000c0aa }, + { 0x0000989c, 0x000000b1, 0x000000b1 }, + { 0x0000989c, 0x00002000, 0x00002000 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + + +static const u32 ar5416Bank6TPC[][3] = { + + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x40ff0000, 0x40ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x00423022, 0x00423022 }, + { 0x0000989c, 0x2014008f, 0x2014008f }, + { 0x0000989c, 0x00c40002, 0x00c40002 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x0001805e, 0x0001805e }, + { 0x0000989c, 0x0000c0ab, 0x0000c0ab }, + { 0x0000989c, 0x000000e1, 0x000000e1 }, + { 0x0000989c, 0x00007080, 0x00007080 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + +static const u32 ar5416Bank7[][2] = { + { 0x0000989c, 0x00000500 }, + { 0x0000989c, 0x00000800 }, + { 0x000098cc, 0x0000000e }, +}; + +static const u32 ar5416Addac[][2] = { + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000010 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x000000c0 }, + {0x0000989c, 0x00000015 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x000098cc, 0x00000000 }, +}; + + +static const u32 ar5416Modes_9160[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf }, + { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0 }, + { 0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x00009850, 0x6d48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6d48b0e2, 0x6d48b0e2 }, + { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e }, + { 0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18 }, + { 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0 }, + { 0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 }, + { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020 }, + { 0x00009960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 }, + { 0x0000a960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 }, + { 0x0000b960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 }, + { 0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, 0x00001120 }, + { 0x0000c9bc, 0x001a0600, 0x001a0600, 0x001a0c00, 0x001a0c00, 0x001a0c00 }, + { 0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329 }, + { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, + { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, + { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880, 0x00000880 }, + { 0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, 0xd03e4788 }, + { 0x0000a20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000b20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000c20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, 0x0a1a7caa }, + { 0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000 }, + { 0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, 0x2e032402 }, + { 0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, 0x4a0a3c06 }, + { 0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, 0x621a540b }, + { 0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, 0x764f6c1b }, + { 0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, 0x845b7a5a }, + { 0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, 0x950f8ccf }, + { 0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, 0xa5cf9b4f }, + { 0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, 0xbddfaf1f }, + { 0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, 0xd1ffc93f }, + { 0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, +}; + +static const u32 ar5416Common_9160[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020015 }, + { 0x00000034, 0x00000005 }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000008 }, + { 0x00000048, 0x00000008 }, + { 0x0000004c, 0x00000010 }, + { 0x00000050, 0x00000000 }, + { 0x00000054, 0x0000001f }, + { 0x00000800, 0x00000000 }, + { 0x00000804, 0x00000000 }, + { 0x00000808, 0x00000000 }, + { 0x0000080c, 0x00000000 }, + { 0x00000810, 0x00000000 }, + { 0x00000814, 0x00000000 }, + { 0x00000818, 0x00000000 }, + { 0x0000081c, 0x00000000 }, + { 0x00000820, 0x00000000 }, + { 0x00000824, 0x00000000 }, + { 0x00001040, 0x002ffc0f }, + { 0x00001044, 0x002ffc0f }, + { 0x00001048, 0x002ffc0f }, + { 0x0000104c, 0x002ffc0f }, + { 0x00001050, 0x002ffc0f }, + { 0x00001054, 0x002ffc0f }, + { 0x00001058, 0x002ffc0f }, + { 0x0000105c, 0x002ffc0f }, + { 0x00001060, 0x002ffc0f }, + { 0x00001064, 0x002ffc0f }, + { 0x00001230, 0x00000000 }, + { 0x00001270, 0x00000000 }, + { 0x00001038, 0x00000000 }, + { 0x00001078, 0x00000000 }, + { 0x000010b8, 0x00000000 }, + { 0x000010f8, 0x00000000 }, + { 0x00001138, 0x00000000 }, + { 0x00001178, 0x00000000 }, + { 0x000011b8, 0x00000000 }, + { 0x000011f8, 0x00000000 }, + { 0x00001238, 0x00000000 }, + { 0x00001278, 0x00000000 }, + { 0x000012b8, 0x00000000 }, + { 0x000012f8, 0x00000000 }, + { 0x00001338, 0x00000000 }, + { 0x00001378, 0x00000000 }, + { 0x000013b8, 0x00000000 }, + { 0x000013f8, 0x00000000 }, + { 0x00001438, 0x00000000 }, + { 0x00001478, 0x00000000 }, + { 0x000014b8, 0x00000000 }, + { 0x000014f8, 0x00000000 }, + { 0x00001538, 0x00000000 }, + { 0x00001578, 0x00000000 }, + { 0x000015b8, 0x00000000 }, + { 0x000015f8, 0x00000000 }, + { 0x00001638, 0x00000000 }, + { 0x00001678, 0x00000000 }, + { 0x000016b8, 0x00000000 }, + { 0x000016f8, 0x00000000 }, + { 0x00001738, 0x00000000 }, + { 0x00001778, 0x00000000 }, + { 0x000017b8, 0x00000000 }, + { 0x000017f8, 0x00000000 }, + { 0x0000103c, 0x00000000 }, + { 0x0000107c, 0x00000000 }, + { 0x000010bc, 0x00000000 }, + { 0x000010fc, 0x00000000 }, + { 0x0000113c, 0x00000000 }, + { 0x0000117c, 0x00000000 }, + { 0x000011bc, 0x00000000 }, + { 0x000011fc, 0x00000000 }, + { 0x0000123c, 0x00000000 }, + { 0x0000127c, 0x00000000 }, + { 0x000012bc, 0x00000000 }, + { 0x000012fc, 0x00000000 }, + { 0x0000133c, 0x00000000 }, + { 0x0000137c, 0x00000000 }, + { 0x000013bc, 0x00000000 }, + { 0x000013fc, 0x00000000 }, + { 0x0000143c, 0x00000000 }, + { 0x0000147c, 0x00000000 }, + { 0x00004030, 0x00000002 }, + { 0x0000403c, 0x00000002 }, + { 0x00007010, 0x00000020 }, + { 0x00007038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x40000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x000080c0, 0x2a82301a }, + { 0x000080c4, 0x05dc01e0 }, + { 0x000080c8, 0x1f402710 }, + { 0x000080cc, 0x01f40000 }, + { 0x000080d0, 0x00001e00 }, + { 0x000080d4, 0x00000000 }, + { 0x000080d8, 0x00400000 }, + { 0x000080e0, 0xffffffff }, + { 0x000080e4, 0x0000ffff }, + { 0x000080e8, 0x003f3f3f }, + { 0x000080ec, 0x00000000 }, + { 0x000080f0, 0x00000000 }, + { 0x000080f4, 0x00000000 }, + { 0x000080f8, 0x00000000 }, + { 0x000080fc, 0x00020000 }, + { 0x00008100, 0x00020000 }, + { 0x00008104, 0x00000001 }, + { 0x00008108, 0x00000052 }, + { 0x0000810c, 0x00000000 }, + { 0x00008110, 0x00000168 }, + { 0x00008118, 0x000100aa }, + { 0x0000811c, 0x00003210 }, + { 0x00008120, 0x08f04800 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0x00000000 }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c4, 0x00000000 }, + { 0x000081d0, 0x00003210 }, + { 0x000081ec, 0x00000000 }, + { 0x000081f0, 0x00000000 }, + { 0x000081f4, 0x00000000 }, + { 0x000081f8, 0x00000000 }, + { 0x000081fc, 0x00000000 }, + { 0x00008200, 0x00000000 }, + { 0x00008204, 0x00000000 }, + { 0x00008208, 0x00000000 }, + { 0x0000820c, 0x00000000 }, + { 0x00008210, 0x00000000 }, + { 0x00008214, 0x00000000 }, + { 0x00008218, 0x00000000 }, + { 0x0000821c, 0x00000000 }, + { 0x00008220, 0x00000000 }, + { 0x00008224, 0x00000000 }, + { 0x00008228, 0x00000000 }, + { 0x0000822c, 0x00000000 }, + { 0x00008230, 0x00000000 }, + { 0x00008234, 0x00000000 }, + { 0x00008238, 0x00000000 }, + { 0x0000823c, 0x00000000 }, + { 0x00008240, 0x00100000 }, + { 0x00008244, 0x0010f400 }, + { 0x00008248, 0x00000100 }, + { 0x0000824c, 0x0001e800 }, + { 0x00008250, 0x00000000 }, + { 0x00008254, 0x00000000 }, + { 0x00008258, 0x00000000 }, + { 0x0000825c, 0x400000ff }, + { 0x00008260, 0x00080922 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x00008300, 0x00000000 }, + { 0x00008304, 0x00000000 }, + { 0x00008308, 0x00000000 }, + { 0x0000830c, 0x00000000 }, + { 0x00008310, 0x00000000 }, + { 0x00008314, 0x00000000 }, + { 0x00008318, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00000000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xad848e19 }, + { 0x00009810, 0x7d14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x00009840, 0x206a01ae }, + { 0x0000984c, 0x1284233c }, + { 0x00009854, 0x00000859 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x05100000 }, + { 0x0000a920, 0x05100000 }, + { 0x0000b920, 0x05100000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280b212 }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5f3ca3de }, + { 0x00009958, 0x2108ecff }, + { 0x00009940, 0x00750604 }, + { 0x0000c95c, 0x004b6a8e }, + { 0x0000c968, 0x000003ce }, + { 0x00009970, 0x190fb515 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x00009980, 0x00000000 }, + { 0x00009984, 0x00000000 }, + { 0x00009988, 0x00000000 }, + { 0x0000998c, 0x00000000 }, + { 0x00009990, 0x00000000 }, + { 0x00009994, 0x00000000 }, + { 0x00009998, 0x00000000 }, + { 0x0000999c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x201fff00 }, + { 0x000099ac, 0x006f0000 }, + { 0x000099b0, 0x03051000 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000200 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099fc, 0x00001042 }, + { 0x00009b00, 0x00000000 }, + { 0x00009b04, 0x00000001 }, + { 0x00009b08, 0x00000002 }, + { 0x00009b0c, 0x00000003 }, + { 0x00009b10, 0x00000004 }, + { 0x00009b14, 0x00000005 }, + { 0x00009b18, 0x00000008 }, + { 0x00009b1c, 0x00000009 }, + { 0x00009b20, 0x0000000a }, + { 0x00009b24, 0x0000000b }, + { 0x00009b28, 0x0000000c }, + { 0x00009b2c, 0x0000000d }, + { 0x00009b30, 0x00000010 }, + { 0x00009b34, 0x00000011 }, + { 0x00009b38, 0x00000012 }, + { 0x00009b3c, 0x00000013 }, + { 0x00009b40, 0x00000014 }, + { 0x00009b44, 0x00000015 }, + { 0x00009b48, 0x00000018 }, + { 0x00009b4c, 0x00000019 }, + { 0x00009b50, 0x0000001a }, + { 0x00009b54, 0x0000001b }, + { 0x00009b58, 0x0000001c }, + { 0x00009b5c, 0x0000001d }, + { 0x00009b60, 0x00000020 }, + { 0x00009b64, 0x00000021 }, + { 0x00009b68, 0x00000022 }, + { 0x00009b6c, 0x00000023 }, + { 0x00009b70, 0x00000024 }, + { 0x00009b74, 0x00000025 }, + { 0x00009b78, 0x00000028 }, + { 0x00009b7c, 0x00000029 }, + { 0x00009b80, 0x0000002a }, + { 0x00009b84, 0x0000002b }, + { 0x00009b88, 0x0000002c }, + { 0x00009b8c, 0x0000002d }, + { 0x00009b90, 0x00000030 }, + { 0x00009b94, 0x00000031 }, + { 0x00009b98, 0x00000032 }, + { 0x00009b9c, 0x00000033 }, + { 0x00009ba0, 0x00000034 }, + { 0x00009ba4, 0x00000035 }, + { 0x00009ba8, 0x00000035 }, + { 0x00009bac, 0x00000035 }, + { 0x00009bb0, 0x00000035 }, + { 0x00009bb4, 0x00000035 }, + { 0x00009bb8, 0x00000035 }, + { 0x00009bbc, 0x00000035 }, + { 0x00009bc0, 0x00000035 }, + { 0x00009bc4, 0x00000035 }, + { 0x00009bc8, 0x00000035 }, + { 0x00009bcc, 0x00000035 }, + { 0x00009bd0, 0x00000035 }, + { 0x00009bd4, 0x00000035 }, + { 0x00009bd8, 0x00000035 }, + { 0x00009bdc, 0x00000035 }, + { 0x00009be0, 0x00000035 }, + { 0x00009be4, 0x00000035 }, + { 0x00009be8, 0x00000035 }, + { 0x00009bec, 0x00000035 }, + { 0x00009bf0, 0x00000035 }, + { 0x00009bf4, 0x00000035 }, + { 0x00009bf8, 0x00000010 }, + { 0x00009bfc, 0x0000001a }, + { 0x0000a210, 0x40806333 }, + { 0x0000a214, 0x00106c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x018830c6 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x001a0bb5 }, + { 0x0000a22c, 0x00000000 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c889af }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00007bb6 }, + { 0x0000a248, 0x0fff3ffc }, + { 0x0000a24c, 0x00000001 }, + { 0x0000a250, 0x0000a000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cc75380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a268, 0x00000001 }, + { 0x0000a26c, 0x0ebae9c6 }, + { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000c26c, 0x0ebae9c6 }, + { 0x0000d270, 0x00820820 }, + { 0x0000a278, 0x1ce739ce }, + { 0x0000a27c, 0x050701ce }, + { 0x0000a338, 0x00000000 }, + { 0x0000a33c, 0x00000000 }, + { 0x0000a340, 0x00000000 }, + { 0x0000a344, 0x00000000 }, + { 0x0000a348, 0x3fffffff }, + { 0x0000a34c, 0x3fffffff }, + { 0x0000a350, 0x3fffffff }, + { 0x0000a354, 0x0003ffff }, + { 0x0000a358, 0x79a8aa33 }, + { 0x0000d35c, 0x07ffffef }, + { 0x0000d360, 0x0fffffe7 }, + { 0x0000d364, 0x17ffffe5 }, + { 0x0000d368, 0x1fffffe4 }, + { 0x0000d36c, 0x37ffffe3 }, + { 0x0000d370, 0x3fffffe3 }, + { 0x0000d374, 0x57ffffe3 }, + { 0x0000d378, 0x5fffffe2 }, + { 0x0000d37c, 0x7fffffe2 }, + { 0x0000d380, 0x7f3c7bba }, + { 0x0000d384, 0xf3307ff0 }, + { 0x0000a388, 0x0c000000 }, + { 0x0000a38c, 0x20202020 }, + { 0x0000a390, 0x20202020 }, + { 0x0000a394, 0x1ce739ce }, + { 0x0000a398, 0x000001ce }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3c8, 0x00000246 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce }, +}; + +static const u32 ar5416Bank0_9160[][2] = { + { 0x000098b0, 0x1e5795e5 }, + { 0x000098e0, 0x02008020 }, +}; + +static const u32 ar5416BB_RfGain_9160[][3] = { + { 0x00009a00, 0x00000000, 0x00000000 }, + { 0x00009a04, 0x00000040, 0x00000040 }, + { 0x00009a08, 0x00000080, 0x00000080 }, + { 0x00009a0c, 0x000001a1, 0x00000141 }, + { 0x00009a10, 0x000001e1, 0x00000181 }, + { 0x00009a14, 0x00000021, 0x000001c1 }, + { 0x00009a18, 0x00000061, 0x00000001 }, + { 0x00009a1c, 0x00000168, 0x00000041 }, + { 0x00009a20, 0x000001a8, 0x000001a8 }, + { 0x00009a24, 0x000001e8, 0x000001e8 }, + { 0x00009a28, 0x00000028, 0x00000028 }, + { 0x00009a2c, 0x00000068, 0x00000068 }, + { 0x00009a30, 0x00000189, 0x000000a8 }, + { 0x00009a34, 0x000001c9, 0x00000169 }, + { 0x00009a38, 0x00000009, 0x000001a9 }, + { 0x00009a3c, 0x00000049, 0x000001e9 }, + { 0x00009a40, 0x00000089, 0x00000029 }, + { 0x00009a44, 0x00000170, 0x00000069 }, + { 0x00009a48, 0x000001b0, 0x00000190 }, + { 0x00009a4c, 0x000001f0, 0x000001d0 }, + { 0x00009a50, 0x00000030, 0x00000010 }, + { 0x00009a54, 0x00000070, 0x00000050 }, + { 0x00009a58, 0x00000191, 0x00000090 }, + { 0x00009a5c, 0x000001d1, 0x00000151 }, + { 0x00009a60, 0x00000011, 0x00000191 }, + { 0x00009a64, 0x00000051, 0x000001d1 }, + { 0x00009a68, 0x00000091, 0x00000011 }, + { 0x00009a6c, 0x000001b8, 0x00000051 }, + { 0x00009a70, 0x000001f8, 0x00000198 }, + { 0x00009a74, 0x00000038, 0x000001d8 }, + { 0x00009a78, 0x00000078, 0x00000018 }, + { 0x00009a7c, 0x00000199, 0x00000058 }, + { 0x00009a80, 0x000001d9, 0x00000098 }, + { 0x00009a84, 0x00000019, 0x00000159 }, + { 0x00009a88, 0x00000059, 0x00000199 }, + { 0x00009a8c, 0x00000099, 0x000001d9 }, + { 0x00009a90, 0x000000d9, 0x00000019 }, + { 0x00009a94, 0x000000f9, 0x00000059 }, + { 0x00009a98, 0x000000f9, 0x00000099 }, + { 0x00009a9c, 0x000000f9, 0x000000d9 }, + { 0x00009aa0, 0x000000f9, 0x000000f9 }, + { 0x00009aa4, 0x000000f9, 0x000000f9 }, + { 0x00009aa8, 0x000000f9, 0x000000f9 }, + { 0x00009aac, 0x000000f9, 0x000000f9 }, + { 0x00009ab0, 0x000000f9, 0x000000f9 }, + { 0x00009ab4, 0x000000f9, 0x000000f9 }, + { 0x00009ab8, 0x000000f9, 0x000000f9 }, + { 0x00009abc, 0x000000f9, 0x000000f9 }, + { 0x00009ac0, 0x000000f9, 0x000000f9 }, + { 0x00009ac4, 0x000000f9, 0x000000f9 }, + { 0x00009ac8, 0x000000f9, 0x000000f9 }, + { 0x00009acc, 0x000000f9, 0x000000f9 }, + { 0x00009ad0, 0x000000f9, 0x000000f9 }, + { 0x00009ad4, 0x000000f9, 0x000000f9 }, + { 0x00009ad8, 0x000000f9, 0x000000f9 }, + { 0x00009adc, 0x000000f9, 0x000000f9 }, + { 0x00009ae0, 0x000000f9, 0x000000f9 }, + { 0x00009ae4, 0x000000f9, 0x000000f9 }, + { 0x00009ae8, 0x000000f9, 0x000000f9 }, + { 0x00009aec, 0x000000f9, 0x000000f9 }, + { 0x00009af0, 0x000000f9, 0x000000f9 }, + { 0x00009af4, 0x000000f9, 0x000000f9 }, + { 0x00009af8, 0x000000f9, 0x000000f9 }, + { 0x00009afc, 0x000000f9, 0x000000f9 }, +}; + +static const u32 ar5416Bank1_9160[][2] = { + { 0x000098b0, 0x02108421 }, + { 0x000098ec, 0x00000008 }, +}; + +static const u32 ar5416Bank2_9160[][2] = { + { 0x000098b0, 0x0e73ff17 }, + { 0x000098e0, 0x00000420 }, +}; + +static const u32 ar5416Bank3_9160[][3] = { + { 0x000098f0, 0x01400018, 0x01c00018 }, +}; + +static const u32 ar5416Bank6_9160[][3] = { + + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x40ff0000, 0x40ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x004210a2, 0x004210a2 }, + { 0x0000989c, 0x0014008f, 0x0014008f }, + { 0x0000989c, 0x00c40003, 0x00c40003 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x0001805e, 0x0001805e }, + { 0x0000989c, 0x0000c0ab, 0x0000c0ab }, + { 0x0000989c, 0x000000f1, 0x000000f1 }, + { 0x0000989c, 0x00002081, 0x00002081 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + +static const u32 ar5416Bank6TPC_9160[][3] = { + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x40ff0000, 0x40ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x00423022, 0x00423022 }, + { 0x0000989c, 0x2014008f, 0x2014008f }, + { 0x0000989c, 0x00c40002, 0x00c40002 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x0001805e, 0x0001805e }, + { 0x0000989c, 0x0000c0ab, 0x0000c0ab }, + { 0x0000989c, 0x000000e1, 0x000000e1 }, + { 0x0000989c, 0x00007080, 0x00007080 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + +static const u32 ar5416Bank7_9160[][2] = { + { 0x0000989c, 0x00000500 }, + { 0x0000989c, 0x00000800 }, + { 0x000098cc, 0x0000000e }, +}; + + +static u32 ar5416Addac_9160[][2] = { + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x000000c0 }, + {0x0000989c, 0x00000018 }, + {0x0000989c, 0x00000004 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x000000c0 }, + {0x0000989c, 0x00000019 }, + {0x0000989c, 0x00000004 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000004 }, + {0x0000989c, 0x00000003 }, + {0x0000989c, 0x00000008 }, + {0x0000989c, 0x00000000 }, + {0x000098cc, 0x00000000 }, +}; + + +static u32 ar5416Addac_91601_1[][2] = { + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x000000c0 }, + {0x0000989c, 0x00000018 }, + {0x0000989c, 0x00000004 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x000000c0 }, + {0x0000989c, 0x00000019 }, + {0x0000989c, 0x00000004 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x000098cc, 0x00000000 }, +}; + + + +static const u32 ar9280Modes_9280[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801080, 0x08400840, 0x06e006e0 }, + { 0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f }, + { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009844, 0x1372161e, 0x1372161e, 0x137216a0, 0x137216a0, 0x137216a0 }, + { 0x00009848, 0x00028566, 0x00028566, 0x00028563, 0x00028563, 0x00028563 }, + { 0x0000a848, 0x00028566, 0x00028566, 0x00028563, 0x00028563, 0x00028563 }, + { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 }, + { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e }, + { 0x00009860, 0x00049d18, 0x00049d18, 0x00049d20, 0x00049d20, 0x00049d18 }, + { 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x5ac64190, 0x5ac64190, 0x5ac64190, 0x5ac64190, 0x5ac64190 }, + { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009944, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010 }, + { 0x00009960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 }, + { 0x0000a960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 }, + { 0x00009964, 0x00000210, 0x00000210, 0x00000210, 0x00000210, 0x00000210 }, + { 0x0000c9b8, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a }, + { 0x0000c9bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c }, + { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, + { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, + { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00009a00, 0x00008184, 0x00008184, 0x00000214, 0x00000214, 0x00000214 }, + { 0x00009a04, 0x00008188, 0x00008188, 0x00000218, 0x00000218, 0x00000218 }, + { 0x00009a08, 0x0000818c, 0x0000818c, 0x00000224, 0x00000224, 0x00000224 }, + { 0x00009a0c, 0x00008190, 0x00008190, 0x00000228, 0x00000228, 0x00000228 }, + { 0x00009a10, 0x00008194, 0x00008194, 0x0000022c, 0x0000022c, 0x0000022c }, + { 0x00009a14, 0x00008200, 0x00008200, 0x00000230, 0x00000230, 0x00000230 }, + { 0x00009a18, 0x00008204, 0x00008204, 0x000002a4, 0x000002a4, 0x000002a4 }, + { 0x00009a1c, 0x00008208, 0x00008208, 0x000002a8, 0x000002a8, 0x000002a8 }, + { 0x00009a20, 0x0000820c, 0x0000820c, 0x000002ac, 0x000002ac, 0x000002ac }, + { 0x00009a24, 0x00008210, 0x00008210, 0x000002b0, 0x000002b0, 0x000002b0 }, + { 0x00009a28, 0x00008214, 0x00008214, 0x000002b4, 0x000002b4, 0x000002b4 }, + { 0x00009a2c, 0x00008280, 0x00008280, 0x000002b8, 0x000002b8, 0x000002b8 }, + { 0x00009a30, 0x00008284, 0x00008284, 0x00000390, 0x00000390, 0x00000390 }, + { 0x00009a34, 0x00008288, 0x00008288, 0x00000394, 0x00000394, 0x00000394 }, + { 0x00009a38, 0x0000828c, 0x0000828c, 0x00000398, 0x00000398, 0x00000398 }, + { 0x00009a3c, 0x00008290, 0x00008290, 0x00000334, 0x00000334, 0x00000334 }, + { 0x00009a40, 0x00008300, 0x00008300, 0x00000338, 0x00000338, 0x00000338 }, + { 0x00009a44, 0x00008304, 0x00008304, 0x000003ac, 0x000003ac, 0x000003ac }, + { 0x00009a48, 0x00008308, 0x00008308, 0x000003b0, 0x000003b0, 0x000003b0 }, + { 0x00009a4c, 0x0000830c, 0x0000830c, 0x000003b4, 0x000003b4, 0x000003b4 }, + { 0x00009a50, 0x00008310, 0x00008310, 0x000003b8, 0x000003b8, 0x000003b8 }, + { 0x00009a54, 0x00008314, 0x00008314, 0x000003a5, 0x000003a5, 0x000003a5 }, + { 0x00009a58, 0x00008380, 0x00008380, 0x000003a9, 0x000003a9, 0x000003a9 }, + { 0x00009a5c, 0x00008384, 0x00008384, 0x000003ad, 0x000003ad, 0x000003ad }, + { 0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194, 0x00008194 }, + { 0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0, 0x000081a0 }, + { 0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c, 0x0000820c }, + { 0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8, 0x000081a8 }, + { 0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284, 0x00008284 }, + { 0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288, 0x00008288 }, + { 0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224, 0x00008224 }, + { 0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290, 0x00008290 }, + { 0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300, 0x00008300 }, + { 0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304, 0x00008304 }, + { 0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308, 0x00008308 }, + { 0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c, 0x0000830c }, + { 0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380, 0x00008380 }, + { 0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384, 0x00008384 }, + { 0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700, 0x00008700 }, + { 0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704, 0x00008704 }, + { 0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708, 0x00008708 }, + { 0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c, 0x0000870c }, + { 0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780, 0x00008780 }, + { 0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784, 0x00008784 }, + { 0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00, 0x00008b00 }, + { 0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04, 0x00008b04 }, + { 0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08, 0x00008b08 }, + { 0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c, 0x00008b0c }, + { 0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b80, 0x00008b80, 0x00008b80 }, + { 0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b84, 0x00008b84, 0x00008b84 }, + { 0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b88, 0x00008b88, 0x00008b88 }, + { 0x00009acc, 0x0000b380, 0x0000b380, 0x00008b8c, 0x00008b8c, 0x00008b8c }, + { 0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b90, 0x00008b90, 0x00008b90 }, + { 0x00009ad4, 0x0000b388, 0x0000b388, 0x00008f80, 0x00008f80, 0x00008f80 }, + { 0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008f84, 0x00008f84, 0x00008f84 }, + { 0x00009adc, 0x0000b390, 0x0000b390, 0x00008f88, 0x00008f88, 0x00008f88 }, + { 0x00009ae0, 0x0000b394, 0x0000b394, 0x00008f8c, 0x00008f8c, 0x00008f8c }, + { 0x00009ae4, 0x0000b398, 0x0000b398, 0x00008f90, 0x00008f90, 0x00008f90 }, + { 0x00009ae8, 0x0000b780, 0x0000b780, 0x0000930c, 0x0000930c, 0x0000930c }, + { 0x00009aec, 0x0000b784, 0x0000b784, 0x00009310, 0x00009310, 0x00009310 }, + { 0x00009af0, 0x0000b788, 0x0000b788, 0x00009384, 0x00009384, 0x00009384 }, + { 0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009388, 0x00009388, 0x00009388 }, + { 0x00009af8, 0x0000b790, 0x0000b790, 0x00009324, 0x00009324, 0x00009324 }, + { 0x00009afc, 0x0000b794, 0x0000b794, 0x00009704, 0x00009704, 0x00009704 }, + { 0x00009b00, 0x0000b798, 0x0000b798, 0x000096a4, 0x000096a4, 0x000096a4 }, + { 0x00009b04, 0x0000d784, 0x0000d784, 0x000096a8, 0x000096a8, 0x000096a8 }, + { 0x00009b08, 0x0000d788, 0x0000d788, 0x00009710, 0x00009710, 0x00009710 }, + { 0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009714, 0x00009714, 0x00009714 }, + { 0x00009b10, 0x0000d790, 0x0000d790, 0x00009720, 0x00009720, 0x00009720 }, + { 0x00009b14, 0x0000f780, 0x0000f780, 0x00009724, 0x00009724, 0x00009724 }, + { 0x00009b18, 0x0000f784, 0x0000f784, 0x00009728, 0x00009728, 0x00009728 }, + { 0x00009b1c, 0x0000f788, 0x0000f788, 0x0000972c, 0x0000972c, 0x0000972c }, + { 0x00009b20, 0x0000f78c, 0x0000f78c, 0x000097a0, 0x000097a0, 0x000097a0 }, + { 0x00009b24, 0x0000f790, 0x0000f790, 0x000097a4, 0x000097a4, 0x000097a4 }, + { 0x00009b28, 0x0000f794, 0x0000f794, 0x000097a8, 0x000097a8, 0x000097a8 }, + { 0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x000097b0, 0x000097b0, 0x000097b0 }, + { 0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x000097b4, 0x000097b4, 0x000097b4 }, + { 0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x000097b8, 0x000097b8, 0x000097b8 }, + { 0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x000097a5, 0x000097a5, 0x000097a5 }, + { 0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x000097a9, 0x000097a9, 0x000097a9 }, + { 0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x000097ad, 0x000097ad, 0x000097ad }, + { 0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x000097b1, 0x000097b1, 0x000097b1 }, + { 0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x000097b5, 0x000097b5, 0x000097b5 }, + { 0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x000097b9, 0x000097b9, 0x000097b9 }, + { 0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x000097c5, 0x000097c5, 0x000097c5 }, + { 0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x000097c9, 0x000097c9, 0x000097c9 }, + { 0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x000097d1, 0x000097d1, 0x000097d1 }, + { 0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x000097d5, 0x000097d5, 0x000097d5 }, + { 0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x000097d9, 0x000097d9, 0x000097d9 }, + { 0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x000097c6, 0x000097c6, 0x000097c6 }, + { 0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x000097ca, 0x000097ca, 0x000097ca }, + { 0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x000097ce, 0x000097ce, 0x000097ce }, + { 0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x000097d2, 0x000097d2, 0x000097d2 }, + { 0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x000097d6, 0x000097d6, 0x000097d6 }, + { 0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x000097c3, 0x000097c3, 0x000097c3 }, + { 0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x000097c7, 0x000097c7, 0x000097c7 }, + { 0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x000097cb, 0x000097cb, 0x000097cb }, + { 0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x000097cf, 0x000097cf, 0x000097cf }, + { 0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x000097d7, 0x000097d7, 0x000097d7 }, + { 0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b98, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b9c, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009ba0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009ba4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009ba8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bac, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bb0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bb4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bb8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bbc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bc0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bc4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bc8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bcc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bd0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bd4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bd8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bdc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009be0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009be4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009be8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bec, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bf0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bf4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bf8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bfc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x0000a204, 0x00000444, 0x00000444, 0x00000444, 0x00000444, 0x00000444 }, + { 0x0000a208, 0x803e4788, 0x803e4788, 0x803e4788, 0x803e4788, 0x803e4788 }, + { 0x0000a20c, 0x000c6019, 0x000c6019, 0x000c6019, 0x000c6019, 0x000c6019 }, + { 0x0000b20c, 0x000c6019, 0x000c6019, 0x000c6019, 0x000c6019, 0x000c6019 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 }, + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00003002, 0x00003002, 0x00003002, 0x00003002, 0x00003002 }, + { 0x0000a308, 0x00006004, 0x00006004, 0x00008009, 0x00008009, 0x00008009 }, + { 0x0000a30c, 0x0000a006, 0x0000a006, 0x0000b00b, 0x0000b00b, 0x0000b00b }, + { 0x0000a310, 0x0000e012, 0x0000e012, 0x0000e012, 0x0000e012, 0x0000e012 }, + { 0x0000a314, 0x00011014, 0x00011014, 0x00012048, 0x00012048, 0x00012048 }, + { 0x0000a318, 0x0001504a, 0x0001504a, 0x0001604a, 0x0001604a, 0x0001604a }, + { 0x0000a31c, 0x0001904c, 0x0001904c, 0x0001a211, 0x0001a211, 0x0001a211 }, + { 0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213, 0x0001e213 }, + { 0x0000a324, 0x00020092, 0x00020092, 0x0002121b, 0x0002121b, 0x0002121b }, + { 0x0000a328, 0x0002410a, 0x0002410a, 0x00024412, 0x00024412, 0x00024412 }, + { 0x0000a32c, 0x0002710c, 0x0002710c, 0x00028414, 0x00028414, 0x00028414 }, + { 0x0000a330, 0x0002b18b, 0x0002b18b, 0x0002b44a, 0x0002b44a, 0x0002b44a }, + { 0x0000a334, 0x0002e1cc, 0x0002e1cc, 0x00030649, 0x00030649, 0x00030649 }, + { 0x0000a338, 0x000321ec, 0x000321ec, 0x0003364b, 0x0003364b, 0x0003364b }, + { 0x0000a33c, 0x000321ec, 0x000321ec, 0x00038a49, 0x00038a49, 0x00038a49 }, + { 0x0000a340, 0x000321ec, 0x000321ec, 0x0003be48, 0x0003be48, 0x0003be48 }, + { 0x0000a344, 0x000321ec, 0x000321ec, 0x0003ee4a, 0x0003ee4a, 0x0003ee4a }, + { 0x0000a348, 0x000321ec, 0x000321ec, 0x00042e88, 0x00042e88, 0x00042e88 }, + { 0x0000a34c, 0x000321ec, 0x000321ec, 0x00046e8a, 0x00046e8a, 0x00046e8a }, + { 0x0000a350, 0x000321ec, 0x000321ec, 0x00049ec9, 0x00049ec9, 0x00049ec9 }, + { 0x0000a354, 0x000321ec, 0x000321ec, 0x0004bf42, 0x0004bf42, 0x0004bf42 }, + { 0x0000784c, 0x0e4f048c, 0x0e4f048c, 0x0e4d048c, 0x0e4d048c, 0x0e4d048c }, + { 0x00007854, 0x12031828, 0x12031828, 0x12035828, 0x12035828, 0x12035828 }, + { 0x00007870, 0x807ec400, 0x807ec400, 0x807ec000, 0x807ec000, 0x807ec000 }, + { 0x0000788c, 0x00010000, 0x00010000, 0x00110000, 0x00110000, 0x00110000 }, +}; + +static const u32 ar9280Common_9280[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020015 }, + { 0x00000034, 0x00000005 }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000008 }, + { 0x00000048, 0x00000008 }, + { 0x0000004c, 0x00000010 }, + { 0x00000050, 0x00000000 }, + { 0x00000054, 0x0000001f }, + { 0x00000800, 0x00000000 }, + { 0x00000804, 0x00000000 }, + { 0x00000808, 0x00000000 }, + { 0x0000080c, 0x00000000 }, + { 0x00000810, 0x00000000 }, + { 0x00000814, 0x00000000 }, + { 0x00000818, 0x00000000 }, + { 0x0000081c, 0x00000000 }, + { 0x00000820, 0x00000000 }, + { 0x00000824, 0x00000000 }, + { 0x00001040, 0x002ffc0f }, + { 0x00001044, 0x002ffc0f }, + { 0x00001048, 0x002ffc0f }, + { 0x0000104c, 0x002ffc0f }, + { 0x00001050, 0x002ffc0f }, + { 0x00001054, 0x002ffc0f }, + { 0x00001058, 0x002ffc0f }, + { 0x0000105c, 0x002ffc0f }, + { 0x00001060, 0x002ffc0f }, + { 0x00001064, 0x002ffc0f }, + { 0x00001230, 0x00000000 }, + { 0x00001270, 0x00000000 }, + { 0x00001038, 0x00000000 }, + { 0x00001078, 0x00000000 }, + { 0x000010b8, 0x00000000 }, + { 0x000010f8, 0x00000000 }, + { 0x00001138, 0x00000000 }, + { 0x00001178, 0x00000000 }, + { 0x000011b8, 0x00000000 }, + { 0x000011f8, 0x00000000 }, + { 0x00001238, 0x00000000 }, + { 0x00001278, 0x00000000 }, + { 0x000012b8, 0x00000000 }, + { 0x000012f8, 0x00000000 }, + { 0x00001338, 0x00000000 }, + { 0x00001378, 0x00000000 }, + { 0x000013b8, 0x00000000 }, + { 0x000013f8, 0x00000000 }, + { 0x00001438, 0x00000000 }, + { 0x00001478, 0x00000000 }, + { 0x000014b8, 0x00000000 }, + { 0x000014f8, 0x00000000 }, + { 0x00001538, 0x00000000 }, + { 0x00001578, 0x00000000 }, + { 0x000015b8, 0x00000000 }, + { 0x000015f8, 0x00000000 }, + { 0x00001638, 0x00000000 }, + { 0x00001678, 0x00000000 }, + { 0x000016b8, 0x00000000 }, + { 0x000016f8, 0x00000000 }, + { 0x00001738, 0x00000000 }, + { 0x00001778, 0x00000000 }, + { 0x000017b8, 0x00000000 }, + { 0x000017f8, 0x00000000 }, + { 0x0000103c, 0x00000000 }, + { 0x0000107c, 0x00000000 }, + { 0x000010bc, 0x00000000 }, + { 0x000010fc, 0x00000000 }, + { 0x0000113c, 0x00000000 }, + { 0x0000117c, 0x00000000 }, + { 0x000011bc, 0x00000000 }, + { 0x000011fc, 0x00000000 }, + { 0x0000123c, 0x00000000 }, + { 0x0000127c, 0x00000000 }, + { 0x000012bc, 0x00000000 }, + { 0x000012fc, 0x00000000 }, + { 0x0000133c, 0x00000000 }, + { 0x0000137c, 0x00000000 }, + { 0x000013bc, 0x00000000 }, + { 0x000013fc, 0x00000000 }, + { 0x0000143c, 0x00000000 }, + { 0x0000147c, 0x00000000 }, + { 0x00004030, 0x00000002 }, + { 0x0000403c, 0x00000002 }, + { 0x00004024, 0x0000001f }, + { 0x00007010, 0x00000033 }, + { 0x00007038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x40000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x00008070, 0x00000000 }, + { 0x000080c0, 0x2a82301a }, + { 0x000080c4, 0x05dc01e0 }, + { 0x000080c8, 0x1f402710 }, + { 0x000080cc, 0x01f40000 }, + { 0x000080d0, 0x00001e00 }, + { 0x000080d4, 0x00000000 }, + { 0x000080d8, 0x00400000 }, + { 0x000080e0, 0xffffffff }, + { 0x000080e4, 0x0000ffff }, + { 0x000080e8, 0x003f3f3f }, + { 0x000080ec, 0x00000000 }, + { 0x000080f0, 0x00000000 }, + { 0x000080f4, 0x00000000 }, + { 0x000080f8, 0x00000000 }, + { 0x000080fc, 0x00020000 }, + { 0x00008100, 0x00020000 }, + { 0x00008104, 0x00000001 }, + { 0x00008108, 0x00000052 }, + { 0x0000810c, 0x00000000 }, + { 0x00008110, 0x00000168 }, + { 0x00008118, 0x000100aa }, + { 0x0000811c, 0x00003210 }, + { 0x00008120, 0x08f04800 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0x00000000 }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c4, 0x00000000 }, + { 0x000081d0, 0x00003210 }, + { 0x000081ec, 0x00000000 }, + { 0x000081f0, 0x00000000 }, + { 0x000081f4, 0x00000000 }, + { 0x000081f8, 0x00000000 }, + { 0x000081fc, 0x00000000 }, + { 0x00008200, 0x00000000 }, + { 0x000082