diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-15 09:40:05 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-15 09:40:05 -0700 |
commit | 2ed0e21b30b53d3a94e204196e523e6c8f732b56 (patch) | |
tree | de2635426477d86338a9469ce09ba0626052288f /drivers/net/wireless/ath | |
parent | 0fa213310cd8fa7a51071cdcf130e26fa56e9549 (diff) | |
parent | 9cbc1cb8cd46ce1f7645b9de249b2ce8460129bb (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1244 commits)
pkt_sched: Rename PSCHED_US2NS and PSCHED_NS2US
ipv4: Fix fib_trie rebalancing
Bluetooth: Fix issue with uninitialized nsh.type in DTL-1 driver
Bluetooth: Fix Kconfig issue with RFKILL integration
PIM-SM: namespace changes
ipv4: update ARPD help text
net: use a deferred timer in rt_check_expire
ieee802154: fix kconfig bool/tristate muckup
bonding: initialization rework
bonding: use is_zero_ether_addr
bonding: network device names are case sensative
bonding: elminate bad refcount code
bonding: fix style issues
bonding: fix destructor
bonding: remove bonding read/write semaphore
bonding: initialize before registration
bonding: bond_create always called with default parameters
x_tables: Convert printk to pr_err
netfilter: conntrack: optional reliable conntrack event delivery
list_nulls: add hlist_nulls_add_head and hlist_nulls_del
...
Diffstat (limited to 'drivers/net/wireless/ath')
72 files changed, 60288 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig new file mode 100644 index 00000000000..d26e7b48531 --- /dev/null +++ b/drivers/net/wireless/ath/Kconfig @@ -0,0 +1,8 @@ +config ATH_COMMON + tristate "Atheros Wireless Cards" + depends on ATH5K || ATH9K || AR9170_USB + +source "drivers/net/wireless/ath/ath5k/Kconfig" +source "drivers/net/wireless/ath/ath9k/Kconfig" +source "drivers/net/wireless/ath/ar9170/Kconfig" + diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile new file mode 100644 index 00000000000..4bb0132ada3 --- /dev/null +++ b/drivers/net/wireless/ath/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_ATH5K) += ath5k/ +obj-$(CONFIG_ATH9K) += ath9k/ +obj-$(CONFIG_AR9170_USB) += ar9170/ + +obj-$(CONFIG_ATH_COMMON) += ath.o +ath-objs := main.o regd.o diff --git a/drivers/net/wireless/ath/ar9170/Kconfig b/drivers/net/wireless/ath/ar9170/Kconfig new file mode 100644 index 00000000000..b99e3263ee6 --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/Kconfig @@ -0,0 +1,18 @@ +config AR9170_USB + tristate "Atheros AR9170 802.11n USB support" + depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL + select FW_LOADER + select ATH_COMMON + help + This is a driver for the Atheros "otus" 802.11n USB devices. + + These devices require additional firmware (2 files). + For now, these files can be downloaded from here: + http://wireless.kernel.org/en/users/Drivers/ar9170 + + If you choose to build a module, it'll be called ar9170usb. + +config AR9170_LEDS + bool + depends on AR9170_USB && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = AR9170_USB) + default y diff --git a/drivers/net/wireless/ath/ar9170/Makefile b/drivers/net/wireless/ath/ar9170/Makefile new file mode 100644 index 00000000000..8d91c7ee321 --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/Makefile @@ -0,0 +1,3 @@ +ar9170usb-objs := usb.o main.o cmd.o mac.o phy.o led.o + +obj-$(CONFIG_AR9170_USB) += ar9170usb.o diff --git a/drivers/net/wireless/ath/ar9170/ar9170.h b/drivers/net/wireless/ath/ar9170/ar9170.h new file mode 100644 index 00000000000..bb97981fb24 --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/ar9170.h @@ -0,0 +1,257 @@ +/* + * Atheros AR9170 driver + * + * Driver specific definitions + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 __AR9170_H +#define __AR9170_H + +#include <linux/completion.h> +#include <linux/spinlock.h> +#include <net/cfg80211.h> +#include <net/mac80211.h> +#ifdef CONFIG_AR9170_LEDS +#include <linux/leds.h> +#endif /* CONFIG_AR9170_LEDS */ +#include "eeprom.h" +#include "hw.h" + +#include "../regd.h" + +#define PAYLOAD_MAX (AR9170_MAX_CMD_LEN/4 - 1) + +enum ar9170_bw { + AR9170_BW_20, + AR9170_BW_40_BELOW, + AR9170_BW_40_ABOVE, + + __AR9170_NUM_BW, +}; + +static inline enum ar9170_bw nl80211_to_ar9170(enum nl80211_channel_type type) +{ + switch (type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + return AR9170_BW_20; + case NL80211_CHAN_HT40MINUS: + return AR9170_BW_40_BELOW; + case NL80211_CHAN_HT40PLUS: + return AR9170_BW_40_ABOVE; + default: + BUG(); + } +} + +enum ar9170_rf_init_mode { + AR9170_RFI_NONE, + AR9170_RFI_WARM, + AR9170_RFI_COLD, +}; + +#define AR9170_MAX_RX_BUFFER_SIZE 8192 + +#ifdef CONFIG_AR9170_LEDS +struct ar9170; + +struct ar9170_led { + struct ar9170 *ar; + struct led_classdev l; + char name[32]; + unsigned int toggled; + bool last_state; + bool registered; +}; + +#endif /* CONFIG_AR9170_LEDS */ + +enum ar9170_device_state { + AR9170_UNKNOWN_STATE, + AR9170_STOPPED, + AR9170_IDLE, + AR9170_STARTED, +}; + +struct ar9170_rxstream_mpdu_merge { + struct ar9170_rx_head plcp; + bool has_plcp; +}; + +#define AR9170_QUEUE_TIMEOUT 64 +#define AR9170_TX_TIMEOUT 8 +#define AR9170_JANITOR_DELAY 128 +#define AR9170_TX_INVALID_RATE 0xffffffff + +struct ar9170 { + struct ieee80211_hw *hw; + struct mutex mutex; + enum ar9170_device_state state; + unsigned long bad_hw_nagger; + + int (*open)(struct ar9170 *); + void (*stop)(struct ar9170 *); + int (*tx)(struct ar9170 *, struct sk_buff *); + int (*exec_cmd)(struct ar9170 *, enum ar9170_cmd, u32 , + void *, u32 , void *); + void (*callback_cmd)(struct ar9170 *, u32 , void *); + int (*flush)(struct ar9170 *); + + /* interface mode settings */ + struct ieee80211_vif *vif; + u8 mac_addr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + + /* beaconing */ + struct sk_buff *beacon; + struct work_struct beacon_work; + + /* cryptographic engine */ + u64 usedkeys; + bool rx_software_decryption; + bool disable_offload; + + /* filter settings */ + struct work_struct filter_config_work; + u64 cur_mc_hash, want_mc_hash; + u32 cur_filter, want_filter; + unsigned long filter_changed; + unsigned int filter_state; + bool sniffer_enabled; + + /* PHY */ + struct ieee80211_channel *channel; + int noise[4]; + + /* power calibration data */ + u8 power_5G_leg[4]; + u8 power_2G_cck[4]; + u8 power_2G_ofdm[4]; + u8 power_5G_ht20[8]; + u8 power_5G_ht40[8]; + u8 power_2G_ht20[8]; + u8 power_2G_ht40[8]; + +#ifdef CONFIG_AR9170_LEDS + struct delayed_work led_work; + struct ar9170_led leds[AR9170_NUM_LEDS]; +#endif /* CONFIG_AR9170_LEDS */ + + /* qos queue settings */ + spinlock_t tx_stats_lock; + struct ieee80211_tx_queue_stats tx_stats[5]; + struct ieee80211_tx_queue_params edcf[5]; + + spinlock_t cmdlock; + __le32 cmdbuf[PAYLOAD_MAX + 1]; + + /* MAC statistics */ + struct ieee80211_low_level_stats stats; + + /* EEPROM */ + struct ar9170_eeprom eeprom; + struct ath_regulatory regulatory; + + /* tx queues - as seen by hw - */ + struct sk_buff_head tx_pending[__AR9170_NUM_TXQ]; + struct sk_buff_head tx_status[__AR9170_NUM_TXQ]; + struct delayed_work tx_janitor; + + /* rxstream mpdu merge */ + struct ar9170_rxstream_mpdu_merge rx_mpdu; + struct sk_buff *rx_failover; + int rx_failover_missing; +}; + +struct ar9170_sta_info { +}; + +#define AR9170_TX_FLAG_WAIT_FOR_ACK BIT(0) +#define AR9170_TX_FLAG_NO_ACK BIT(1) +#define AR9170_TX_FLAG_BLOCK_ACK BIT(2) + +struct ar9170_tx_info { + unsigned long timeout; + unsigned int flags; +}; + +#define IS_STARTED(a) (((struct ar9170 *)a)->state >= AR9170_STARTED) +#define IS_ACCEPTING_CMD(a) (((struct ar9170 *)a)->state >= AR9170_IDLE) + +#define AR9170_FILTER_CHANGED_MODE BIT(0) +#define AR9170_FILTER_CHANGED_MULTICAST BIT(1) +#define AR9170_FILTER_CHANGED_FRAMEFILTER BIT(2) + +/* exported interface */ +void *ar9170_alloc(size_t priv_size); +int ar9170_register(struct ar9170 *ar, struct device *pdev); +void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb); +void ar9170_unregister(struct ar9170 *ar); +void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb); +void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len); +int ar9170_nag_limiter(struct ar9170 *ar); + +/* MAC */ +int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +int ar9170_init_mac(struct ar9170 *ar); +int ar9170_set_qos(struct ar9170 *ar); +int ar9170_update_multicast(struct ar9170 *ar); +int ar9170_update_frame_filter(struct ar9170 *ar); +int ar9170_set_operating_mode(struct ar9170 *ar); +int ar9170_set_beacon_timers(struct ar9170 *ar); +int ar9170_set_dyn_sifs_ack(struct ar9170 *ar); +int ar9170_set_slot_time(struct ar9170 *ar); +int ar9170_set_basic_rates(struct ar9170 *ar); +int ar9170_set_hwretry_limit(struct ar9170 *ar, u32 max_retry); +int ar9170_update_beacon(struct ar9170 *ar); +void ar9170_new_beacon(struct work_struct *work); +int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype, + u8 keyidx, u8 *keydata, int keylen); +int ar9170_disable_key(struct ar9170 *ar, u8 id); + +/* LEDs */ +#ifdef CONFIG_AR9170_LEDS +int ar9170_register_leds(struct ar9170 *ar); +void ar9170_unregister_leds(struct ar9170 *ar); +#endif /* CONFIG_AR9170_LEDS */ +int ar9170_init_leds(struct ar9170 *ar); +int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state); + +/* PHY / RF */ +int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band); +int ar9170_init_rf(struct ar9170 *ar); +int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, + enum ar9170_rf_init_mode rfi, enum ar9170_bw bw); + +#endif /* __AR9170_H */ diff --git a/drivers/net/wireless/ath/ar9170/cmd.c b/drivers/net/wireless/ath/ar9170/cmd.c new file mode 100644 index 00000000000..f57a6200167 --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/cmd.c @@ -0,0 +1,129 @@ +/* + * Atheros AR9170 driver + * + * Basic HW register/memory/command access functions + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 "ar9170.h" +#include "cmd.h" + +int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len) +{ + int err; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return 0; + + err = ar->exec_cmd(ar, AR9170_CMD_WMEM, len, (u8 *) data, 0, NULL); + if (err) + printk(KERN_DEBUG "%s: writing memory failed\n", + wiphy_name(ar->hw->wiphy)); + return err; +} + +int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val) +{ + __le32 buf[2] = { + cpu_to_le32(reg), + cpu_to_le32(val), + }; + int err; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return 0; + + err = ar->exec_cmd(ar, AR9170_CMD_WREG, sizeof(buf), + (u8 *) buf, 0, NULL); + if (err) + printk(KERN_DEBUG "%s: writing reg %#x (val %#x) failed\n", + wiphy_name(ar->hw->wiphy), reg, val); + return err; +} + +static int ar9170_read_mreg(struct ar9170 *ar, int nregs, + const u32 *regs, u32 *out) +{ + int i, err; + __le32 *offs, *res; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return 0; + + /* abuse "out" for the register offsets, must be same length */ + offs = (__le32 *)out; + for (i = 0; i < nregs; i++) + offs[i] = cpu_to_le32(regs[i]); + + /* also use the same buffer for the input */ + res = (__le32 *)out; + + err = ar->exec_cmd(ar, AR9170_CMD_RREG, + 4 * nregs, (u8 *)offs, + 4 * nregs, (u8 *)res); + if (err) + return err; + + /* convert result to cpu endian */ + for (i = 0; i < nregs; i++) + out[i] = le32_to_cpu(res[i]); + + return 0; +} + +int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val) +{ + return ar9170_read_mreg(ar, 1, ®, val); +} + +int ar9170_echo_test(struct ar9170 *ar, u32 v) +{ + __le32 echobuf = cpu_to_le32(v); + __le32 echores; + int err; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return -ENODEV; + + err = ar->exec_cmd(ar, AR9170_CMD_ECHO, + 4, (u8 *)&echobuf, + 4, (u8 *)&echores); + if (err) + return err; + + if (echobuf != echores) + return -EINVAL; + + return 0; +} diff --git a/drivers/net/wireless/ath/ar9170/cmd.h b/drivers/net/wireless/ath/ar9170/cmd.h new file mode 100644 index 00000000000..a4f0e50e52b --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/cmd.h @@ -0,0 +1,91 @@ +/* + * Atheros AR9170 driver + * + * Basic HW register/memory/command access functions + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 __CMD_H +#define __CMD_H + +#include "ar9170.h" + +/* basic HW access */ +int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len); +int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val); +int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val); +int ar9170_echo_test(struct ar9170 *ar, u32 v); + +/* + * Macros to facilitate writing multiple registers in a single + * write-combining USB command. Note that when the first group + * fails the whole thing will fail without any others attempted, + * but you won't know which write in the group failed. + */ +#define ar9170_regwrite_begin(ar) \ +do { \ + int __nreg = 0, __err = 0; \ + struct ar9170 *__ar = ar; + +#define ar9170_regwrite(r, v) do { \ + __ar->cmdbuf[2 * __nreg + 1] = cpu_to_le32(r); \ + __ar->cmdbuf[2 * __nreg + 2] = cpu_to_le32(v); \ + __nreg++; \ + if ((__nreg >= PAYLOAD_MAX/2)) { \ + if (IS_ACCEPTING_CMD(__ar)) \ + __err = ar->exec_cmd(__ar, AR9170_CMD_WREG, \ + 8 * __nreg, \ + (u8 *) &__ar->cmdbuf[1], \ + 0, NULL); \ + __nreg = 0; \ + if (__err) \ + goto __regwrite_out; \ + } \ +} while (0) + +#define ar9170_regwrite_finish() \ +__regwrite_out : \ + if (__nreg) { \ + if (IS_ACCEPTING_CMD(__ar)) \ + __err = ar->exec_cmd(__ar, AR9170_CMD_WREG, \ + 8 * __nreg, \ + (u8 *) &__ar->cmdbuf[1], \ + 0, NULL); \ + __nreg = 0; \ + } + +#define ar9170_regwrite_result() \ + __err; \ +} while (0); + +#endif /* __CMD_H */ diff --git a/drivers/net/wireless/ath/ar9170/eeprom.h b/drivers/net/wireless/ath/ar9170/eeprom.h new file mode 100644 index 00000000000..d2c8cc83f1d --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/eeprom.h @@ -0,0 +1,179 @@ +/* + * Atheros AR9170 driver + * + * EEPROM layout + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 __AR9170_EEPROM_H +#define __AR9170_EEPROM_H + +#define AR5416_MAX_CHAINS 2 +#define AR5416_MODAL_SPURS 5 + +struct ar9170_eeprom_modal { + __le32 antCtrlChain[AR5416_MAX_CHAINS]; + __le32 antCtrlCommon; + s8 antennaGainCh[AR5416_MAX_CHAINS]; + u8 switchSettling; + u8 txRxAttenCh[AR5416_MAX_CHAINS]; + u8 rxTxMarginCh[AR5416_MAX_CHAINS]; + s8 adcDesiredSize; + s8 pgaDesiredSize; + u8 xlnaGainCh[AR5416_MAX_CHAINS]; + u8 txEndToXpaOff; + u8 txEndToRxOn; + u8 txFrameToXpaOn; + u8 thresh62; + s8 noiseFloorThreshCh[AR5416_MAX_CHAINS]; + u8 xpdGain; + u8 xpd; + s8 iqCalICh[AR5416_MAX_CHAINS]; + s8 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 reserved[22]; + struct spur_channel { + __le16 spurChan; + u8 spurRangeLow; + u8 spurRangeHigh; + } __packed spur_channels[AR5416_MODAL_SPURS]; +} __packed; + +#define AR5416_NUM_PD_GAINS 4 +#define AR5416_PD_GAIN_ICEPTS 5 + +struct ar9170_calibration_data_per_freq { + u8 pwr_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; + u8 vpd_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; +} __packed; + +#define AR5416_NUM_5G_CAL_PIERS 8 +#define AR5416_NUM_2G_CAL_PIERS 4 + +#define AR5416_NUM_5G_TARGET_PWRS 8 +#define AR5416_NUM_2G_CCK_TARGET_PWRS 3 +#define AR5416_NUM_2G_OFDM_TARGET_PWRS 4 +#define AR5416_MAX_NUM_TGT_PWRS 8 + +struct ar9170_calibration_target_power_legacy { + u8 freq; + u8 power[4]; +} __packed; + +struct ar9170_calibration_target_power_ht { + u8 freq; + u8 power[8]; +} __packed; + +#define AR5416_NUM_CTLS 24 + +struct ar9170_calctl_edges { + u8 channel; +#define AR9170_CALCTL_EDGE_FLAGS 0xC0 + u8 power_flags; +} __packed; + +#define AR5416_NUM_BAND_EDGES 8 + +struct ar9170_calctl_data { + struct ar9170_calctl_edges + control_edges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES]; +} __packed; + + +struct ar9170_eeprom { + __le16 length; + __le16 checksum; + __le16 version; + u8 operating_flags; +#define AR9170_OPFLAG_5GHZ 1 +#define AR9170_OPFLAG_2GHZ 2 + u8 misc; + __le16 reg_domain[2]; + u8 mac_address[6]; + u8 rx_mask; + u8 tx_mask; + __le16 rf_silent; + __le16 bluetooth_options; + __le16 device_capabilities; + __le32 build_number; + u8 deviceType; + u8 reserved[33]; + + u8 customer_data[64]; + + struct ar9170_eeprom_modal + modal_header[2]; + + u8 cal_freq_pier_5G[AR5416_NUM_5G_CAL_PIERS]; + u8 cal_freq_pier_2G[AR5416_NUM_2G_CAL_PIERS]; + + struct ar9170_calibration_data_per_freq + cal_pier_data_5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS], + cal_pier_data_2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS]; + + /* power calibration data */ + struct ar9170_calibration_target_power_legacy + cal_tgt_pwr_5G[AR5416_NUM_5G_TARGET_PWRS]; + struct ar9170_calibration_target_power_ht + cal_tgt_pwr_5G_ht20[AR5416_NUM_5G_TARGET_PWRS], + cal_tgt_pwr_5G_ht40[AR5416_NUM_5G_TARGET_PWRS]; + + struct ar9170_calibration_target_power_legacy + cal_tgt_pwr_2G_cck[AR5416_NUM_2G_CCK_TARGET_PWRS], + cal_tgt_pwr_2G_ofdm[AR5416_NUM_2G_OFDM_TARGET_PWRS]; + struct ar9170_calibration_target_power_ht + cal_tgt_pwr_2G_ht20[AR5416_NUM_2G_OFDM_TARGET_PWRS], + cal_tgt_pwr_2G_ht40[AR5416_NUM_2G_OFDM_TARGET_PWRS]; + + /* conformance testing limits */ + u8 ctl_index[AR5416_NUM_CTLS]; + struct ar9170_calctl_data + ctl_data[AR5416_NUM_CTLS]; + + u8 pad; + __le16 subsystem_id; +} __packed; + +#endif /* __AR9170_EEPROM_H */ diff --git a/drivers/net/wireless/ath/ar9170/hw.h b/drivers/net/wireless/ath/ar9170/hw.h new file mode 100644 index 00000000000..6cbfb2f8339 --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/hw.h @@ -0,0 +1,426 @@ +/* + * Atheros AR9170 driver + * + * Hardware-specific definitions + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 __AR9170_HW_H +#define __AR9170_HW_H + +#define AR9170_MAX_CMD_LEN 64 + +enum ar9170_cmd { + AR9170_CMD_RREG = 0x00, + AR9170_CMD_WREG = 0x01, + AR9170_CMD_RMEM = 0x02, + AR9170_CMD_WMEM = 0x03, + AR9170_CMD_BITAND = 0x04, + AR9170_CMD_BITOR = 0x05, + AR9170_CMD_EKEY = 0x28, + AR9170_CMD_DKEY = 0x29, + AR9170_CMD_FREQUENCY = 0x30, + AR9170_CMD_RF_INIT = 0x31, + AR9170_CMD_SYNTH = 0x32, + AR9170_CMD_FREQ_START = 0x33, + AR9170_CMD_ECHO = 0x80, + AR9170_CMD_TALLY = 0x81, + AR9170_CMD_TALLY_APD = 0x82, + AR9170_CMD_CONFIG = 0x83, + AR9170_CMD_RESET = 0x90, + AR9170_CMD_DKRESET = 0x91, + AR9170_CMD_DKTX_STATUS = 0x92, + AR9170_CMD_FDC = 0xA0, + AR9170_CMD_WREEPROM = 0xB0, + AR9170_CMD_WFLASH = 0xB0, + AR9170_CMD_FLASH_ERASE = 0xB1, + AR9170_CMD_FLASH_PROG = 0xB2, + AR9170_CMD_FLASH_CHKSUM = 0xB3, + AR9170_CMD_FLASH_READ = 0xB4, + AR9170_CMD_FW_DL_INIT = 0xB5, + AR9170_CMD_MEM_WREEPROM = 0xBB, +}; + +/* endpoints */ +#define AR9170_EP_TX 1 +#define AR9170_EP_RX 2 +#define AR9170_EP_IRQ 3 +#define AR9170_EP_CMD 4 + +#define AR9170_EEPROM_START 0x1600 + +#define AR9170_GPIO_REG_BASE 0x1d0100 +#define AR9170_GPIO_REG_PORT_TYPE AR9170_GPIO_REG_BASE +#define AR9170_GPIO_REG_DATA (AR9170_GPIO_REG_BASE + 4) +#define AR9170_NUM_LEDS 2 + + +#define AR9170_USB_REG_BASE 0x1e1000 +#define AR9170_USB_REG_DMA_CTL (AR9170_USB_REG_BASE + 0x108) +#define AR9170_DMA_CTL_ENABLE_TO_DEVICE 0x1 +#define AR9170_DMA_CTL_ENABLE_FROM_DEVICE 0x2 +#define AR9170_DMA_CTL_HIGH_SPEED 0x4 +#define AR9170_DMA_CTL_PACKET_MODE 0x8 + +#define AR9170_USB_REG_MAX_AGG_UPLOAD (AR9170_USB_REG_BASE + 0x110) +#define AR9170_USB_REG_UPLOAD_TIME_CTL (AR9170_USB_REG_BASE + 0x114) + + + +#define AR9170_MAC_REG_BASE 0x1c3000 + +#define AR9170_MAC_REG_TSF_L (AR9170_MAC_REG_BASE + 0x514) +#define AR9170_MAC_REG_TSF_H (AR9170_MAC_REG_BASE + 0x518) + +#define AR9170_MAC_REG_ATIM_WINDOW (AR9170_MAC_REG_BASE + 0x51C) +#define AR9170_MAC_REG_BCN_PERIOD (AR9170_MAC_REG_BASE + 0x520) +#define AR9170_MAC_REG_PRETBTT (AR9170_MAC_REG_BASE + 0x524) + +#define AR9170_MAC_REG_MAC_ADDR_L (AR9170_MAC_REG_BASE + 0x610) +#define AR9170_MAC_REG_MAC_ADDR_H (AR9170_MAC_REG_BASE + 0x614) +#define AR9170_MAC_REG_BSSID_L (AR9170_MAC_REG_BASE + 0x618) +#define AR9170_MAC_REG_BSSID_H (AR9170_MAC_REG_BASE + 0x61c) + +#define AR9170_MAC_REG_GROUP_HASH_TBL_L (AR9170_MAC_REG_BASE + 0x624) +#define AR9170_MAC_REG_GROUP_HASH_TBL_H (AR9170_MAC_REG_BASE + 0x628) + +#define AR9170_MAC_REG_RX_TIMEOUT (AR9170_MAC_REG_BASE + 0x62C) + +#define AR9170_MAC_REG_BASIC_RATE (AR9170_MAC_REG_BASE + 0x630) +#define AR9170_MAC_REG_MANDATORY_RATE (AR9170_MAC_REG_BASE + 0x634) +#define AR9170_MAC_REG_RTS_CTS_RATE (AR9170_MAC_REG_BASE + 0x638) +#define AR9170_MAC_REG_BACKOFF_PROTECT (AR9170_MAC_REG_BASE + 0x63c) +#define AR9170_MAC_REG_RX_THRESHOLD (AR9170_MAC_REG_BASE + 0x640) +#define AR9170_MAC_REG_RX_PE_DELAY (AR9170_MAC_REG_BASE + 0x64C) + +#define AR9170_MAC_REG_DYNAMIC_SIFS_ACK (AR9170_MAC_REG_BASE + 0x658) +#define AR9170_MAC_REG_SNIFFER (AR9170_MAC_REG_BASE + 0x674) +#define AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC BIT(0) +#define AR9170_MAC_REG_SNIFFER_DEFAULTS 0x02000000 +#define AR9170_MAC_REG_ENCRYPTION (AR9170_MAC_REG_BASE + 0x678) +#define AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE BIT(3) +#define AR9170_MAC_REG_ENCRYPTION_DEFAULTS 0x70 + +#define AR9170_MAC_REG_MISC_680 (AR9170_MAC_REG_BASE + 0x680) +#define AR9170_MAC_REG_TX_UNDERRUN (AR9170_MAC_REG_BASE + 0x688) + +#define AR9170_MAC_REG_FRAMETYPE_FILTER (AR9170_MAC_REG_BASE + 0x68c) +#define AR9170_MAC_REG_FTF_ASSOC_REQ BIT(0) +#define AR9170_MAC_REG_FTF_ASSOC_RESP BIT(1) +#define AR9170_MAC_REG_FTF_REASSOC_REQ BIT(2) +#define AR9170_MAC_REG_FTF_REASSOC_RESP BIT(3) +#define AR9170_MAC_REG_FTF_PRB_REQ BIT(4) +#define AR9170_MAC_REG_FTF_PRB_RESP BIT(5) +#define AR9170_MAC_REG_FTF_BIT6 BIT(6) +#define AR9170_MAC_REG_FTF_BIT7 BIT(7) +#define AR9170_MAC_REG_FTF_BEACON BIT(8) +#define AR9170_MAC_REG_FTF_ATIM BIT(9) +#define AR9170_MAC_REG_FTF_DEASSOC BIT(10) +#define AR9170_MAC_REG_FTF_AUTH BIT(11) +#define AR9170_MAC_REG_FTF_DEAUTH BIT(12) +#define AR9170_MAC_REG_FTF_BIT13 BIT(13) +#define AR9170_MAC_REG_FTF_BIT14 BIT(14) +#define AR9170_MAC_REG_FTF_BIT15 BIT(15) +#define AR9170_MAC_REG_FTF_BAR BIT(24) +#define AR9170_MAC_REG_FTF_BIT25 BIT(25) +#define AR9170_MAC_REG_FTF_PSPOLL BIT(26) +#define AR9170_MAC_REG_FTF_RTS BIT(27) +#define AR9170_MAC_REG_FTF_CTS BIT(28) +#define AR9170_MAC_REG_FTF_ACK BIT(29) +#define AR9170_MAC_REG_FTF_CFE BIT(30) +#define AR9170_MAC_REG_FTF_CFE_ACK BIT(31) +#define AR9170_MAC_REG_FTF_DEFAULTS 0x0500ffff +#define AR9170_MAC_REG_FTF_MONITOR 0xfd00ffff + +#define AR9170_MAC_REG_RX_TOTAL (AR9170_MAC_REG_BASE + 0x6A0) +#define AR9170_MAC_REG_RX_CRC32 (AR9170_MAC_REG_BASE + 0x6A4) +#define AR9170_MAC_REG_RX_CRC16 (AR9170_MAC_REG_BASE + 0x6A8) +#define AR9170_MAC_REG_RX_ERR_DECRYPTION_UNI (AR9170_MAC_REG_BASE + 0x6AC) +#define AR9170_MAC_REG_RX_OVERRUN (AR9170_MAC_REG_BASE + 0x6B0) +#define AR9170_MAC_REG_RX_ERR_DECRYPTION_MUL (AR9170_MAC_REG_BASE + 0x6BC) +#define AR9170_MAC_REG_TX_RETRY (AR9170_MAC_REG_BASE + 0x6CC) +#define AR9170_MAC_REG_TX_TOTAL (AR9170_MAC_REG_BASE + 0x6F4) + + +#define AR9170_MAC_REG_ACK_EXTENSION (AR9170_MAC_REG_BASE + 0x690) +#define AR9170_MAC_REG_EIFS_AND_SIFS (AR9170_MAC_REG_BASE + 0x698) + +#define AR9170_MAC_REG_SLOT_TIME (AR9170_MAC_REG_BASE + 0x6F0) + +#define AR9170_MAC_REG_POWERMANAGEMENT (AR9170_MAC_REG_BASE + 0x700) +#define AR9170_MAC_REG_POWERMGT_IBSS 0xe0 +#define AR9170_MAC_REG_POWERMGT_AP 0xa1 +#define AR9170_MAC_REG_POWERMGT_STA 0x2 +#define AR9170_MAC_REG_POWERMGT_AP_WDS 0x3 +#define AR9170_MAC_REG_POWERMGT_DEFAULTS (0xf << 24) + +#define AR9170_MAC_REG_ROLL_CALL_TBL_L (AR9170_MAC_REG_BASE + 0x704) +#define AR9170_MAC_REG_ROLL_CALL_TBL_H (AR9170_MAC_REG_BASE + 0x708) + +#define AR9170_MAC_REG_AC0_CW (AR9170_MAC_REG_BASE + 0xB00) +#define AR9170_MAC_REG_AC1_CW (AR9170_MAC_REG_BASE + 0xB04) +#define AR9170_MAC_REG_AC2_CW (AR9170_MAC_REG_BASE + 0xB08) +#define AR9170_MAC_REG_AC3_CW (AR9170_MAC_REG_BASE + 0xB0C) +#define AR9170_MAC_REG_AC4_CW (AR9170_MAC_REG_BASE + 0xB10) +#define AR9170_MAC_REG_AC1_AC0_AIFS (AR9170_MAC_REG_BASE + 0xB14) +#define AR9170_MAC_REG_AC3_AC2_AIFS (AR9170_MAC_REG_BASE + 0xB18) + +#define AR9170_MAC_REG_RETRY_MAX (AR9170_MAC_REG_BASE + 0xB28) + +#define AR9170_MAC_REG_FCS_SELECT (AR9170_MAC_REG_BASE + 0xBB0) +#define AR9170_MAC_FCS_SWFCS 0x1 +#define AR9170_MAC_FCS_FIFO_PROT 0x4 + + +#define AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND (AR9170_MAC_REG_BASE + 0xB30) + +#define AR9170_MAC_REG_AC1_AC0_TXOP (AR9170_MAC_REG_BASE + 0xB44) +#define AR9170_MAC_REG_AC3_AC2_TXOP (AR9170_MAC_REG_BASE + 0xB48) + +#define AR9170_MAC_REG_AMPDU_FACTOR (AR9170_MAC_REG_BASE + 0xB9C) +#define AR9170_MAC_REG_AMPDU_DENSITY (AR9170_MAC_REG_BASE + 0xBA0) + +#define AR9170_MAC_REG_ACK_TABLE (AR9170_MAC_REG_BASE + 0xC00) +#define AR9170_MAC_REG_AMPDU_RX_THRESH (AR9170_MAC_REG_BASE + 0xC50) + +#define AR9170_MAC_REG_TXRX_MPI (AR9170_MAC_REG_BASE + 0xD7C) +#define AR9170_MAC_TXRX_MPI_TX_MPI_MASK 0x0000000f +#define AR9170_MAC_TXRX_MPI_TX_TO_MASK 0x0000fff0 +#define AR9170_MAC_TXRX_MPI_RX_MPI_MASK 0x000f0000 +#define AR9170_MAC_TXRX_MPI_RX_TO_MASK 0xfff00000 + +#define AR9170_MAC_REG_BCN_ADDR (AR9170_MAC_REG_BASE + 0xD84) +#define AR9170_MAC_REG_BCN_LENGTH (AR9170_MAC_REG_BASE + 0xD88) +#define AR9170_MAC_REG_BCN_PLCP (AR9170_MAC_REG_BASE + 0xD90) +#define AR9170_MAC_REG_BCN_CTRL (AR9170_MAC_REG_BASE + 0xD94) +#define AR9170_MAC_REG_BCN_HT1 (AR9170_MAC_REG_BASE + 0xDA0) +#define AR9170_MAC_REG_BCN_HT2 (AR9170_MAC_REG_BASE + 0xDA4) + + +#define AR9170_PWR_REG_BASE 0x1D4000 + +#define AR9170_PWR_REG_CLOCK_SEL (AR9170_PWR_REG_BASE + 0x008) +#define AR9170_PWR_CLK_AHB_40MHZ 0 +#define AR9170_PWR_CLK_AHB_20_22MHZ 1 +#define AR9170_PWR_CLK_AHB_40_44MHZ 2 +#define AR9170_PWR_CLK_AHB_80_88MHZ 3 +#define AR9170_PWR_CLK_DAC_160_INV_DLY 0x70 + + +/* put beacon here in memory */ +#define AR9170_BEACON_BUFFER_ADDRESS 0x117900 + + +struct ar9170_tx_control { + __le16 length; + __le16 mac_control; + __le32 phy_control; + u8 frame_data[0]; +} __packed; + +/* these are either-or */ +#define AR9170_TX_MAC_PROT_RTS 0x0001 +#define AR9170_TX_MAC_PROT_CTS 0x0002 + +#define AR9170_TX_MAC_NO_ACK 0x0004 +/* if unset, MAC will only do SIFS space before frame */ +#define AR9170_TX_MAC_BACKOFF 0x0008 +#define AR9170_TX_MAC_BURST 0x0010 +#define AR9170_TX_MAC_AGGR 0x0020 + +/* encryption is a two-bit field */ +#define AR9170_TX_MAC_ENCR_NONE 0x0000 +#define AR9170_TX_MAC_ENCR_RC4 0x0040 +#define AR9170_TX_MAC_ENCR_CENC 0x0080 +#define AR9170_TX_MAC_ENCR_AES 0x00c0 + +#define AR9170_TX_MAC_MMIC 0x0100 +#define AR9170_TX_MAC_HW_DURATION 0x0200 +#define AR9170_TX_MAC_QOS_SHIFT 10 +#define AR9170_TX_MAC_QOS_MASK (3 << AR9170_TX_MAC_QOS_SHIFT) +#define AR9170_TX_MAC_AGGR_QOS_BIT1 0x0400 +#define AR9170_TX_MAC_AGGR_QOS_BIT2 0x0800 +#define AR9170_TX_MAC_DISABLE_TXOP 0x1000 +#define AR9170_TX_MAC_TXOP_RIFS 0x2000 +#define AR9170_TX_MAC_IMM_AMPDU 0x4000 +#define AR9170_TX_MAC_RATE_PROBE 0x8000 + +/* either-or */ +#define AR9170_TX_PHY_MOD_CCK 0x00000000 +#define AR9170_TX_PHY_MOD_OFDM 0x00000001 +#define AR9170_TX_PHY_MOD_HT 0x00000002 + +/* depends on modulation */ +#define AR9170_TX_PHY_SHORT_PREAMBLE 0x00000004 +#define AR9170_TX_PHY_GREENFIELD 0x00000004 + +#define AR9170_TX_PHY_BW_SHIFT 3 +#define AR9170_TX_PHY_BW_MASK (3 << AR9170_TX_PHY_BW_SHIFT) +#define AR9170_TX_PHY_BW_20MHZ 0 +#define AR9170_TX_PHY_BW_40MHZ 2 +#define AR9170_TX_PHY_BW_40MHZ_DUP 3 + +#define AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT 6 +#define AR9170_TX_PHY_TX_HEAVY_CLIP_MASK (7 << AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT) + +#define AR9170_TX_PHY_TX_PWR_SHIFT 9 +#define AR9170_TX_PHY_TX_PWR_MASK (0x3f << AR9170_TX_PHY_TX_PWR_SHIFT) + +/* not part of the hw-spec */ +#define AR9170_TX_PHY_QOS_SHIFT 25 +#define AR9170_TX_PHY_QOS_MASK (3 << AR9170_TX_PHY_QOS_SHIFT) + +#define AR9170_TX_PHY_TXCHAIN_SHIFT 15 +#define AR9170_TX_PHY_TXCHAIN_MASK (7 << AR9170_TX_PHY_TXCHAIN_SHIFT) +#define AR9170_TX_PHY_TXCHAIN_1 1 +/* use for cck, ofdm 6/9/12/18/24 and HT if capable */ +#define AR9170_TX_PHY_TXCHAIN_2 5 + +#define AR9170_TX_PHY_MCS_SHIFT 18 +#define AR9170_TX_PHY_MCS_MASK (0x7f << AR9170_TX_PHY_MCS_SHIFT) + +#define AR9170_TX_PHY_SHORT_GI 0x80000000 + +struct ar9170_rx_head { + u8 plcp[12]; +} __packed; + +struct ar9170_rx_phystatus { + union { + struct { + u8 rssi_ant0, rssi_ant1, rssi_ant2, + rssi_ant0x, rssi_ant1x, rssi_ant2x, + rssi_combined; + } __packed; + u8 rssi[7]; + } __packed; + + u8 evm_stream0[6], evm_stream1[6]; + u8 phy_err; +} __packed; + +struct ar9170_rx_macstatus { + u8 SAidx, DAidx; + u8 error; + u8 status; +} __packed; + +#define AR9170_ENC_ALG_NONE 0x0 +#define AR9170_ENC_ALG_WEP64 0x1 +#define AR9170_ENC_ALG_TKIP 0x2 +#define AR9170_ENC_ALG_AESCCMP 0x4 +#define AR9170_ENC_ALG_WEP128 0x5 +#define AR9170_ENC_ALG_WEP256 0x6 +#define AR9170_ENC_ALG_CENC 0x7 + +#define AR9170_RX_ENC_SOFTWARE 0x8 + +static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_macstatus *t) +{ + return (t->SAidx & 0xc0) >> 4 | + (t->DAidx & 0xc0) >> 6; +} + +#define AR9170_RX_STATUS_MODULATION_MASK 0x03 +#define AR9170_RX_STATUS_MODULATION_CCK 0x00 +#define AR9170_RX_STATUS_MODULATION_OFDM 0x01 +#define AR9170_RX_STATUS_MODULATION_HT 0x02 +#define AR9170_RX_STATUS_MODULATION_DUPOFDM 0x03 + +/* depends on modulation */ +#define AR9170_RX_STATUS_SHORT_PREAMBLE 0x08 +#define AR9170_RX_STATUS_GREENFIELD 0x08 + +#define AR9170_RX_STATUS_MPDU_MASK 0x30 +#define AR9170_RX_STATUS_MPDU_SINGLE 0x00 +#define AR9170_RX_STATUS_MPDU_FIRST 0x20 +#define AR9170_RX_STATUS_MPDU_MIDDLE 0x30 +#define AR9170_RX_STATUS_MPDU_LAST 0x10 + +#define AR9170_RX_ERROR_RXTO 0x01 +#define AR9170_RX_ERROR_OVERRUN 0x02 +#define AR9170_RX_ERROR_DECRYPT 0x04 +#define AR9170_RX_ERROR_FCS 0x08 +#define AR9170_RX_ERROR_WRONG_RA 0x10 +#define AR9170_RX_ERROR_PLCP 0x20 +#define AR9170_RX_ERROR_MMIC 0x40 +#define AR9170_RX_ERROR_FATAL 0x80 + +struct ar9170_cmd_tx_status { + u8 dst[ETH_ALEN]; + __le32 rate; + __le16 status; +} __packed; + +#define AR9170_TX_STATUS_COMPLETE 0x00 +#define AR9170_TX_STATUS_RETRY 0x01 +#define AR9170_TX_STATUS_FAILED 0x02 + +struct ar9170_cmd_ba_failed_count { + __le16 failed; + __le16 rate; +} __packed; + +struct ar9170_cmd_response { + u8 flag; + u8 type; + __le16 padding; + + union { + struct ar9170_cmd_tx_status tx_status; + struct ar9170_cmd_ba_failed_count ba_fail_cnt; + u8 data[0]; + }; +} __packed; + +/* QoS */ + +/* mac80211 queue to HW/FW map */ +static const u8 ar9170_qos_hwmap[4] = { 3, 2, 0, 1 }; + +/* HW/FW queue to mac80211 map */ +static const u8 ar9170_qos_mac80211map[4] = { 2, 3, 1, 0 }; + +enum ar9170_txq { + AR9170_TXQ_BE, + AR9170_TXQ_BK, + AR9170_TXQ_VI, + AR9170_TXQ_VO, + + __AR9170_NUM_TXQ, +}; + +#define AR9170_TXQ_DEPTH 32 +#define AR9170_TX_MAX_PENDING 128 + +#endif /* __AR9170_HW_H */ diff --git a/drivers/net/wireless/ath/ar9170/led.c b/drivers/net/wireless/ath/ar9170/led.c new file mode 100644 index 00000000000..63fda6cd210 --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/led.c @@ -0,0 +1,178 @@ +/* + * Atheros AR9170 driver + * + * LED handling + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 "ar9170.h" +#include "cmd.h" + +int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state) +{ + return ar9170_write_reg(ar, AR9170_GPIO_REG_DATA, led_state); +} + +int ar9170_init_leds(struct ar9170 *ar) +{ + int err; + + /* disable LEDs */ + /* GPIO [0/1 mode: output, 2/3: input] */ + err = ar9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3); + if (err) + goto out; + + /* GPIO 0/1 value: off */ + err = ar9170_set_leds_state(ar, 0); + +out: + return err; +} + +#ifdef CONFIG_AR9170_LEDS +static void ar9170_update_leds(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, led_work.work); + int i, tmp, blink_delay = 1000; + u32 led_val = 0; + bool rerun = false; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return ; + + mutex_lock(&ar->mutex); + for (i = 0; i < AR9170_NUM_LEDS; i++) + if (ar->leds[i].registered && ar->leds[i].toggled) { + led_val |= 1 << i; + + tmp = 70 + 200 / (ar->leds[i].toggled); + if (tmp < blink_delay) + blink_delay = tmp; + + if (ar->leds[i].toggled > 1) + ar->leds[i].toggled = 0; + + rerun = true; + } + + ar9170_set_leds_state(ar, led_val); + mutex_unlock(&ar->mutex); + + if (rerun) + queue_delayed_work(ar->hw->workqueue, &ar->led_work, + msecs_to_jiffies(blink_delay)); +} + +static void ar9170_led_brightness_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct ar9170_led *arl = container_of(led, struct ar9170_led, l); + struct ar9170 *ar = arl->ar; + + if (unlikely(!arl->registered)) + return ; + + if (arl->last_state != !!brightness) { + arl->toggled++; + arl->last_state = !!brightness; + } + + if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled)) + queue_delayed_work(ar->hw->workqueue, &ar->led_work, HZ/10); +} + +static int ar9170_register_led(struct ar9170 *ar, int i, char *name, + char *trigger) +{ + int err; + + snprintf(ar->leds[i].name, sizeof(ar->leds[i].name), + "ar9170-%s::%s", wiphy_name(ar->hw->wiphy), name); + + ar->leds[i].ar = ar; + ar->leds[i].l.name = ar->leds[i].name; + ar->leds[i].l.brightness_set = ar9170_led_brightness_set; + ar->leds[i].l.brightness = 0; + ar->leds[i].l.default_trigger = trigger; + + err = led_classdev_register(wiphy_dev(ar->hw->wiphy), + &ar->leds[i].l); + if (err) + printk(KERN_ERR "%s: failed to register %s LED (%d).\n", + wiphy_name(ar->hw->wiphy), ar->leds[i].name, err); + else + ar->leds[i].registered = true; + + return err; +} + +void ar9170_unregister_leds(struct ar9170 *ar) +{ + int i; + + for (i = 0; i < AR9170_NUM_LEDS; i++) + if (ar->leds[i].registered) { + led_classdev_unregister(&ar->leds[i].l); + ar->leds[i].registered = false; + ar->leds[i].toggled = 0; + } + + cancel_delayed_work_sync(&ar->led_work); +} + +int ar9170_register_leds(struct ar9170 *ar) +{ + int err; + + INIT_DELAYED_WORK(&ar->led_work, ar9170_update_leds); + + err = ar9170_register_led(ar, 0, "tx", + ieee80211_get_tx_led_name(ar->hw)); + if (err) + goto fail; + + err = ar9170_register_led(ar, 1, "assoc", + ieee80211_get_assoc_led_name(ar->hw)); + if (err) + goto fail; + + return 0; + +fail: + ar9170_unregister_leds(ar); + return err; +} + +#endif /* CONFIG_AR9170_LEDS */ diff --git a/drivers/net/wireless/ath/ar9170/mac.c b/drivers/net/wireless/ath/ar9170/mac.c new file mode 100644 index 00000000000..d9f1f46de18 --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/mac.c @@ -0,0 +1,524 @@ +/* + * Atheros AR9170 driver + * + * MAC programming + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 "ar9170.h" +#include "cmd.h" + +int ar9170_set_dyn_sifs_ack(struct ar9170 *ar) +{ + u32 val; + + if (conf_is_ht40(&ar->hw->conf)) + val = 0x010a; + else { + if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) + val = 0x105; + else + val = 0x104; + } + + return ar9170_write_reg(ar, AR9170_MAC_REG_DYNAMIC_SIFS_ACK, val); +} + +int ar9170_set_slot_time(struct ar9170 *ar) +{ + u32 slottime = 20; + + if (!ar->vif) + return 0; + + if ((ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ) || + ar->vif->bss_conf.use_short_slot) + slottime = 9; + + return ar9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME, slottime << 10); +} + +int ar9170_set_basic_rates(struct ar9170 *ar) +{ + u8 cck, ofdm; + + if (!ar->vif) + return 0; + + ofdm = ar->vif->bss_conf.basic_rates >> 4; + + /* FIXME: is still necessary? */ + if (ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ) + cck = 0; + else + cck = ar->vif->bss_conf.basic_rates & 0xf; + + return ar9170_write_reg(ar, AR9170_MAC_REG_BASIC_RATE, + ofdm << 8 | cck); +} + +int ar9170_set_qos(struct ar9170 *ar) +{ + ar9170_regwrite_begin(ar); + + ar9170_regwrite(AR9170_MAC_REG_AC0_CW, ar->edcf[0].cw_min | + (ar->edcf[0].cw_max << 16)); + ar9170_regwrite(AR9170_MAC_REG_AC1_CW, ar->edcf[1].cw_min | + (ar->edcf[1].cw_max << 16)); + ar9170_regwrite(AR9170_MAC_REG_AC2_CW, ar->edcf[2].cw_min | + (ar->edcf[2].cw_max << 16)); + ar9170_regwrite(AR9170_MAC_REG_AC3_CW, ar->edcf[3].cw_min | + (ar->edcf[3].cw_max << 16)); + ar9170_regwrite(AR9170_MAC_REG_AC4_CW, ar->edcf[4].cw_min | + (ar->edcf[4].cw_max << 16)); + + ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_AIFS, + ((ar->edcf[0].aifs * 9 + 10)) | + ((ar->edcf[1].aifs * 9 + 10) << 12) | + ((ar->edcf[2].aifs * 9 + 10) << 24)); + ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_AIFS, + ((ar->edcf[2].aifs * 9 + 10) >> 8) | + ((ar->edcf[3].aifs * 9 + 10) << 4) | + ((ar->edcf[4].aifs * 9 + 10) << 16)); + + ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP, + ar->edcf[0].txop | ar->edcf[1].txop << 16); + ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP, + ar->edcf[1].txop | ar->edcf[3].txop << 16); + + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +static int ar9170_set_ampdu_density(struct ar9170 *ar, u8 mpdudensity) +{ + u32 val; + + /* don't allow AMPDU density > 8us */ + if (mpdudensity > 6) + return -EINVAL; + + /* Watch out! Otus uses slightly different density values. */ + val = 0x140a00 | (mpdudensity ? (mpdudensity + 1) : 0); + + ar9170_regwrite_begin(ar); + ar9170_regwrite(AR9170_MAC_REG_AMPDU_DENSITY, val); + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +int ar9170_init_mac(struct ar9170 *ar) +{ + ar9170_regwrite_begin(ar); + + ar9170_regwrite(AR9170_MAC_REG_ACK_EXTENSION, 0x40); + + ar9170_regwrite(AR9170_MAC_REG_RETRY_MAX, 0); + + /* enable MMIC */ + ar9170_regwrite(AR9170_MAC_REG_SNIFFER, + AR9170_MAC_REG_SNIFFER_DEFAULTS); + + ar9170_regwrite(AR9170_MAC_REG_RX_THRESHOLD, 0xc1f80); + + ar9170_regwrite(AR9170_MAC_REG_RX_PE_DELAY, 0x70); + ar9170_regwrite(AR9170_MAC_REG_EIFS_AND_SIFS, 0xa144000); + ar9170_regwrite(AR9170_MAC_REG_SLOT_TIME, 9 << 10); + + /* CF-END mode */ + ar9170_regwrite(0x1c3b2c, 0x19000000); + + /* NAV protects ACK only (in TXOP) */ + ar9170_regwrite(0x1c3b38, 0x201); + + /* Set Beacon PHY CTRL's TPC to 0x7, TA1=1 */ + /* OTUS set AM to 0x1 */ + ar9170_regwrite(AR9170_MAC_REG_BCN_HT1, 0x8000170); + + ar9170_regwrite(AR9170_MAC_REG_BACKOFF_PROTECT, 0x105); + + /* AGG test code*/ + /* Aggregation MAX number and timeout */ + ar9170_regwrite(0x1c3b9c, 0x10000a); + + ar9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER, + AR9170_MAC_REG_FTF_DEFAULTS); + + /* Enable deaggregator, response in sniffer mode */ + ar9170_regwrite(0x1c3c40, 0x1 | 1<<30); + + /* rate sets */ + ar9170_regwrite(AR9170_MAC_REG_BASIC_RATE, 0x150f); + ar9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, 0x150f); + ar9170_regwrite(AR9170_MAC_REG_RTS_CTS_RATE, 0x10b01bb); + + /* MIMO response control */ + ar9170_regwrite(0x1c3694, 0x4003C1E);/* bit 26~28 otus-AM */ + + /* switch MAC to OTUS interface */ + ar9170_regwrite(0x1c3600, 0x3); + + ar9170_regwrite(AR9170_MAC_REG_AMPDU_RX_THRESH, 0xffff); + + /* set PHY register read timeout (??) */ + ar9170_regwrite(AR9170_MAC_REG_MISC_680, 0xf00008); + + /* Disable Rx TimeOut, workaround for BB. */ + ar9170_regwrite(AR9170_MAC_REG_RX_TIMEOUT, 0x0); + + /* Set CPU clock frequency to 88/80MHz */ + ar9170_regwrite(AR9170_PWR_REG_CLOCK_SEL, + AR9170_PWR_CLK_AHB_80_88MHZ | + AR9170_PWR_CLK_DAC_160_INV_DLY); + + /* Set WLAN DMA interrupt mode: generate int per packet */ + ar9170_regwrite(AR9170_MAC_REG_TXRX_MPI, 0x110011); + + ar9170_regwrite(AR9170_MAC_REG_FCS_SELECT, + AR9170_MAC_FCS_FIFO_PROT); + + /* Disables the CF_END frame, undocumented register */ + ar9170_regwrite(AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND, + 0x141E0F48); + + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +static int ar9170_set_mac_reg(struct ar9170 *ar, const u32 reg, const u8 *mac) +{ + static const u8 zero[ETH_ALEN] = { 0 }; + + if (!mac) + mac = zero; + + ar9170_regwrite_begin(ar); + + ar9170_regwrite(reg, + (mac[3] << 24) | (mac[2] << 16) | + (mac[1] << 8) | mac[0]); + + ar9170_regwrite(reg + 4, (mac[5] << 8) | mac[4]); + + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +int ar9170_update_multicast(struct ar9170 *ar) +{ + int err; + + ar9170_regwrite_begin(ar); + ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, + ar->want_mc_hash >> 32); + ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, + ar->want_mc_hash); + + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + + if (err) + return err; + + ar->cur_mc_hash = ar->want_mc_hash; + + return 0; +} + +int ar9170_update_frame_filter(struct ar9170 *ar) +{ + int err; + + err = ar9170_write_reg(ar, AR9170_MAC_REG_FRAMETYPE_FILTER, + ar->want_filter); + + if (err) + return err; + + ar->cur_filter = ar->want_filter; + + return 0; +} + +static int ar9170_set_promiscouous(struct ar9170 *ar) +{ + u32 encr_mode, sniffer; + int err; + + err = ar9170_read_reg(ar, AR9170_MAC_REG_SNIFFER, &sniffer); + if (err) + return err; + + err = ar9170_read_reg(ar, AR9170_MAC_REG_ENCRYPTION, &encr_mode); + if (err) + return err; + + if (ar->sniffer_enabled) { + sniffer |= AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC; + + /* + * Rx decryption works in place. + * + * If we don't disable it, the hardware will render all + * encrypted frames which are encrypted with an unknown + * key useless. + */ + + encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE; + ar->sniffer_enabled = true; + } else { + sniffer &= ~AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC; + + if (ar->rx_software_decryption) + encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE; + else + encr_mode &= ~AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE; + } + + ar9170_regwrite_begin(ar); + ar9170_regwrite(AR9170_MAC_REG_ENCRYPTION, encr_mode); + ar9170_regwrite(AR9170_MAC_REG_SNIFFER, sniffer); + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +int ar9170_set_operating_mode(struct ar9170 *ar) +{ + u32 pm_mode = AR9170_MAC_REG_POWERMGT_DEFAULTS; + u8 *mac_addr, *bssid; + int err; + + if (ar->vif) { + mac_addr = ar->mac_addr; + bssid = ar->bssid; + + switch (ar->vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + pm_mode |= AR9170_MAC_REG_POWERMGT_IBSS; + break; + case NL80211_IFTYPE_AP: + pm_mode |= AR9170_MAC_REG_POWERMGT_AP; + break; + case NL80211_IFTYPE_WDS: + pm_mode |= AR9170_MAC_REG_POWERMGT_AP_WDS; + break; + case NL80211_IFTYPE_MONITOR: + ar->sniffer_enabled = true; + ar->rx_software_decryption = true; + break; + default: + pm_mode |= AR9170_MAC_REG_POWERMGT_STA; + break; + } + } else { + mac_addr = NULL; + bssid = NULL; + } + + err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_MAC_ADDR_L, mac_addr); + if (err) + return err; + + err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_BSSID_L, bssid); + if (err) + return err; + + err = ar9170_set_promiscouous(ar); + if (err) + return err; + + /* set AMPDU density to 8us. */ + err = ar9170_set_ampdu_density(ar, 6); + if (err) + return err; + + ar9170_regwrite_begin(ar); + + ar9170_regwrite(AR9170_MAC_REG_POWERMANAGEMENT, pm_mode); + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +int ar9170_set_hwretry_limit(struct ar9170 *ar, unsigned int max_retry) +{ + u32 tmp = min_t(u32, 0x33333, max_retry * 0x11111); + + return ar9170_write_reg(ar, AR9170_MAC_REG_RETRY_MAX, tmp); +} + +int ar9170_set_beacon_timers(struct ar9170 *ar) +{ + u32 v = 0; + u32 pretbtt = 0; + + if (ar->vif) { + v |= ar->vif->bss_conf.beacon_int; + + switch (ar->vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + v |= BIT(25); + break; + case NL80211_IFTYPE_AP: + v |= BIT(24); + pretbtt = (ar->vif->bss_conf.beacon_int - 6) << 16; + break; + default: + break; + } + + v |= ar->vif->bss_conf.dtim_period << 16; + } + + ar9170_regwrite_begin(ar); + + ar9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt); + ar9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v); + ar9170_regwrite_finish(); + return ar9170_regwrite_result(); +} + +int ar9170_update_beacon(struct ar9170 *ar) +{ + struct sk_buff *skb; + __le32 *data, *old = NULL; + u32 word; + int i; + + skb = ieee80211_beacon_get(ar->hw, ar->vif); + if (!skb) + return -ENOMEM; + + data = (__le32 *)skb->data; + if (ar->beacon) + old = (__le32 *)ar->beacon->data; + + ar9170_regwrite_begin(ar); + for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) { + /* + * XXX: This accesses beyond skb data for up + * to the last 3 bytes!! + */ + + if (old && (data[i] == old[i])) + continue; + + word = le32_to_cpu(data[i]); + ar9170_regwrite(AR9170_BEACON_BUFFER_ADDRESS + 4 * i, word); + } + + /* XXX: use skb->cb info */ + if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) + ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP, + ((skb->len + 4) << (3 + 16)) + 0x0400); + else + ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP, + ((skb->len + 4) << 16) + 0x001b); + + ar9170_regwrite(AR9170_MAC_REG_BCN_LENGTH, skb->len + 4); + ar9170_regwrite(AR9170_MAC_REG_BCN_ADDR, AR9170_BEACON_BUFFER_ADDRESS); + ar9170_regwrite(AR9170_MAC_REG_BCN_CTRL, 1); + + ar9170_regwrite_finish(); + + dev_kfree_skb(ar->beacon); + ar->beacon = skb; + + return ar9170_regwrite_result(); +} + +void ar9170_new_beacon(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, + beacon_work); + struct sk_buff *skb; + + if (unlikely(!IS_STARTED(ar))) + return ; + + mutex_lock(&ar->mutex); + + if (!ar->vif) + goto out; + + ar9170_update_beacon(ar); + + rcu_read_lock(); + while ((skb = ieee80211_get_buffered_bc(ar->hw, ar->vif))) + ar9170_op_tx(ar->hw, skb); + + rcu_read_unlock(); + + out: + mutex_unlock(&ar->mutex); +} + +int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype, + u8 keyidx, u8 *keydata, int keylen) +{ + __le32 vals[7]; + static const u8 bcast[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + u8 dummy; + + mac = mac ? : bcast; + + vals[0] = cpu_to_le32((keyidx << 16) + id); + vals[1] = cpu_to_le32(mac[1] << 24 | mac[0] << 16 | ktype); + vals[2] = cpu_to_le32(mac[5] << 24 | mac[4] << 16 | + mac[3] << 8 | mac[2]); + memset(&vals[3], 0, 16); + if (keydata) + memcpy(&vals[3], keydata, keylen); + + return ar->exec_cmd(ar, AR9170_CMD_EKEY, + sizeof(vals), (u8 *)vals, + 1, &dummy); +} + +int ar9170_disable_key(struct ar9170 *ar, u8 id) +{ + __le32 val = cpu_to_le32(id); + u8 dummy; + + return ar->exec_cmd(ar, AR9170_CMD_EKEY, + sizeof(val), (u8 *)&val, + 1, &dummy); +} diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c new file mode 100644 index 00000000000..9d38cf60a0d --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -0,0 +1,2210 @@ +/* + * Atheros AR9170 driver + * + * mac80211 interaction code + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009, Christian Lamparter <chunkeey@web.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 <linux/init.h> +#include <linux/module.h> +#include <linux/etherdevice.h> +#include <net/mac80211.h> +#include "ar9170.h" +#include "hw.h" +#include "cmd.h" + +static int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +#define RATE(_bitrate, _hw_rate, _txpidx, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate) | (_txpidx) << 4, \ +} + +static struct ieee80211_rate __ar9170_ratetable[] = { + RATE(10, 0, 0, 0), + RATE(20, 1, 1, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(55, 2, 2, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(110, 3, 3, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(60, 0xb, 0, 0), + RATE(90, 0xf, 0, 0), + RATE(120, 0xa, 0, 0), + RATE(180, 0xe, 0, 0), + RATE(240, 0x9, 0, 0), + RATE(360, 0xd, 1, 0), + RATE(480, 0x8, 2, 0), + RATE(540, 0xc, 3, 0), +}; +#undef RATE + +#define ar9170_g_ratetable (__ar9170_ratetable + 0) +#define ar9170_g_ratetable_size 12 +#define ar9170_a_ratetable (__ar9170_ratetable + 4) +#define ar9170_a_ratetable_size 8 + +/* + * NB: The hw_value is used as an index into the ar9170_phy_freq_params + * array in phy.c so that we don't have to do frequency lookups! + */ +#define CHAN(_freq, _idx) { \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 18, /* XXX */ \ +} + +static struct ieee80211_channel ar9170_2ghz_chantable[] = { + CHAN(2412, 0), + CHAN(2417, 1), + CHAN(2422, 2), + CHAN(2427, 3), + CHAN(2432, 4), + CHAN(2437, 5), + CHAN(2442, 6), + CHAN(2447, 7), + CHAN(2452, 8), + CHAN(2457, 9), + CHAN(2462, 10), + CHAN(2467, 11), + CHAN(2472, 12), + CHAN(2484, 13), +}; + +static struct ieee80211_channel ar9170_5ghz_chantable[] = { + CHAN(4920, 14), + CHAN(4940, 15), + CHAN(4960, 16), + CHAN(4980, 17), + CHAN(5040, 18), + CHAN(5060, 19), + CHAN(5080, 20), + CHAN(5180, 21), + CHAN(5200, 22), + CHAN(5220, 23), + CHAN(5240, 24), + CHAN(5260, 25), + CHAN(5280, 26), + CHAN(5300, 27), + CHAN(5320, 28), + CHAN(5500, 29), + CHAN(5520, 30), + CHAN(5540, 31), + CHAN(5560, 32), + CHAN(5580, 33), + CHAN(5600, 34), + CHAN(5620, 35), + CHAN(5640, 36), + CHAN(5660, 37), + CHAN(5680, 38), + CHAN(5700, 39), + CHAN(5745, 40), + CHAN(5765, 41), + CHAN(5785, 42), + CHAN(5805, 43), + CHAN(5825, 44), + CHAN(5170, 45), + CHAN(5190, 46), + CHAN(5210, 47), + CHAN(5230, 48), +}; +#undef CHAN + +#define AR9170_HT_CAP \ +{ \ + .ht_supported = true, \ + .cap = IEEE80211_HT_CAP_MAX_AMSDU | \ + IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ + IEEE80211_HT_CAP_SGI_40 | \ + IEEE80211_HT_CAP_DSSSCCK40 | \ + IEEE80211_HT_CAP_SM_PS, \ + .ampdu_factor = 3, \ + .ampdu_density = 6, \ + .mcs = { \ + .rx_mask = { 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, }, \ + }, \ +} + +static struct ieee80211_supported_band ar9170_band_2GHz = { + .channels = ar9170_2ghz_chantable, + .n_channels = ARRAY_SIZE(ar9170_2ghz_chantable), + .bitrates = ar9170_g_ratetable, + .n_bitrates = ar9170_g_ratetable_size, + .ht_cap = AR9170_HT_CAP, +}; + +static struct ieee80211_supported_band ar9170_band_5GHz = { + .channels = ar9170_5ghz_chantable, + .n_channels = ARRAY_SIZE(ar9170_5ghz_chantable), + .bitrates = ar9170_a_ratetable, + .n_bitrates = ar9170_a_ratetable_size, + .ht_cap = AR9170_HT_CAP, +}; + +static void ar9170_tx(struct ar9170 *ar); + +#ifdef AR9170_QUEUE_DEBUG +static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ar9170_tx_control *txc = (void *) skb->data; + struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); + struct ar9170_tx_info *arinfo = (void *) txinfo->rate_driver_data; + struct ieee80211_hdr *hdr = (void *) txc->frame_data; + + printk(KERN_DEBUG "%s: => FRAME [skb:%p, q:%d, DA:[%pM] flags:%x " + "mac_ctrl:%04x, phy_ctrl:%08x, timeout:[%d ms]]\n", + wiphy_name(ar->hw->wiphy), skb, skb_get_queue_mapping(skb), + ieee80211_get_DA(hdr), arinfo->flags, + le16_to_cpu(txc->mac_control), le32_to_cpu(txc->phy_control), + jiffies_to_msecs(arinfo->timeout - jiffies)); +} + +static void __ar9170_dump_txqueue(struct ar9170 *ar, + struct sk_buff_head *queue) +{ + struct sk_buff *skb; + int i = 0; + + printk(KERN_DEBUG "---[ cut here ]---\n"); + printk(KERN_DEBUG "%s: %d entries in queue.\n", + wiphy_name(ar->hw->wiphy), skb_queue_len(queue)); + + skb_queue_walk(queue, skb) { + printk(KERN_DEBUG "index:%d => \n", i++); + ar9170_print_txheader(ar, skb); + } + if (i != skb_queue_len(queue)) + printk(KERN_DEBUG "WARNING: queue frame counter " + "mismatch %d != %d\n", skb_queue_len(queue), i); + printk(KERN_DEBUG "---[ end ]---\n"); +} + +static void ar9170_dump_txqueue(struct ar9170 *ar, + struct sk_buff_head *queue) +{ + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + __ar9170_dump_txqueue(ar, queue); + spin_unlock_irqrestore(&queue->lock, flags); +} + +static void __ar9170_dump_txstats(struct ar9170 *ar) +{ + int i; + + printk(KERN_DEBUG "%s: QoS queue stats\n", + wiphy_name(ar->hw->wiphy)); + + for (i = 0; i < __AR9170_NUM_TXQ; i++) + printk(KERN_DEBUG "%s: queue:%d limit:%d len:%d waitack:%d\n", + wiphy_name(ar->hw->wiphy), i, ar->tx_stats[i].limit, + ar->tx_stats[i].len, skb_queue_len(&ar->tx_status[i])); +} + +static void ar9170_dump_txstats(struct ar9170 *ar) +{ + unsigned long flags; + + spin_lock_irqsave(&ar->tx_stats_lock, flags); + __ar9170_dump_txstats(ar); + spin_unlock_irqrestore(&ar->tx_stats_lock, flags); +} +#endif /* AR9170_QUEUE_DEBUG */ + +/* caller must guarantee exclusive access for _bin_ queue. */ +static void ar9170_recycle_expired(struct ar9170 *ar, + struct sk_buff_head *queue, + struct sk_buff_head *bin) +{ + struct sk_buff *skb, *old = NULL; + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + while ((skb = skb_peek(queue))) { + struct ieee80211_tx_info *txinfo; + struct ar9170_tx_info *arinfo; + + txinfo = IEEE80211_SKB_CB(skb); + arinfo = (void *) txinfo->rate_driver_data; + + if (time_is_before_jiffies(arinfo->timeout)) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: [%ld > %ld] frame expired => " + "recycle \n", wiphy_name(ar->hw->wiphy), + jiffies, arinfo->timeout); + ar9170_print_txheader(ar, skb); +#endif /* AR9170_QUEUE_DEBUG */ + __skb_unlink(skb, queue); + __skb_queue_tail(bin, skb); + } else { + break; + } + + if (unlikely(old == skb)) { + /* bail out - queue is shot. */ + + WARN_ON(1); + break; + } + old = skb; + } + spin_unlock_irqrestore(&queue->lock, flags); +} + +static void ar9170_tx_status(struct ar9170 *ar, struct sk_buff *skb, + u16 tx_status) +{ + struct ieee80211_tx_info *txinfo; + unsigned int retries = 0; + + txinfo = IEEE80211_SKB_CB(skb); + ieee80211_tx_info_clear_status(txinfo); + + switch (tx_status) { + case AR9170_TX_STATUS_RETRY: + retries = 2; + case AR9170_TX_STATUS_COMPLETE: + txinfo->flags |= IEEE80211_TX_STAT_ACK; + break; + + case AR9170_TX_STATUS_FAILED: + retries = ar->hw->conf.long_frame_max_tx_count; + break; + + default: + printk(KERN_ERR "%s: invalid tx_status response (%x).\n", + wiphy_name(ar->hw->wiphy), tx_status); + break; + } + + txinfo->status.rates[0].count = retries + 1; + skb_pull(skb, sizeof(struct ar9170_tx_control)); + ieee80211_tx_status_irqsafe(ar->hw, skb); +} + +void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ar9170_tx_info *arinfo = (void *) info->rate_driver_data; + unsigned int queue = skb_get_queue_mapping(skb); + unsigned long flags; + + spin_lock_irqsave(&ar->tx_stats_lock, flags); + ar->tx_stats[queue].len--; + + if (skb_queue_empty(&ar->tx_pending[queue])) { +#ifdef AR9170_QUEUE_STOP_DEBUG + printk(KERN_DEBUG "%s: wake queue %d\n", + wiphy_name(ar->hw->wiphy), queue); + __ar9170_dump_txstats(ar); +#endif /* AR9170_QUEUE_STOP_DEBUG */ + ieee80211_wake_queue(ar->hw, queue); + } + spin_unlock_irqrestore(&ar->tx_stats_lock, flags); + + if (arinfo->flags & AR9170_TX_FLAG_BLOCK_ACK) { + dev_kfree_skb_any(skb); + } else if (arinfo->flags & AR9170_TX_FLAG_WAIT_FOR_ACK) { + arinfo->timeout = jiffies + + msecs_to_jiffies(AR9170_TX_TIMEOUT); + + skb_queue_tail(&ar->tx_status[queue], skb); + } else if (arinfo->flags & AR9170_TX_FLAG_NO_ACK) { + ar9170_tx_status(ar, skb, AR9170_TX_STATUS_FAILED); + } else { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: unsupported frame flags!\n", + wiphy_name(ar->hw->wiphy)); + ar9170_print_txheader(ar, skb); +#endif /* AR9170_QUEUE_DEBUG */ + dev_kfree_skb_any(skb); + } + + if (!ar->tx_stats[queue].len && + !skb_queue_empty(&ar->tx_pending[queue])) { + ar9170_tx(ar); + } +} + +static struct sk_buff *ar9170_get_queued_skb(struct ar9170 *ar, + const u8 *mac, + struct sk_buff_head *queue, + const u32 rate) +{ + unsigned long flags; + struct sk_buff *skb; + + /* + * Unfortunately, the firmware does not tell to which (queued) frame + * this transmission status report belongs to. + * + * So we have to make risky guesses - with the scarce information + * the firmware provided (-> destination MAC, and phy_control) - + * and hope that we picked the right one... + */ + + spin_lock_irqsave(&queue->lock, flags); + skb_queue_walk(queue, skb) { + struct ar9170_tx_control *txc = (void *) skb->data; + struct ieee80211_hdr *hdr = (void *) txc->frame_data; + u32 r; + + if (mac && compare_ether_addr(ieee80211_get_DA(hdr), mac)) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: skip frame => DA %pM != %pM\n", + wiphy_name(ar->hw->wiphy), mac, + ieee80211_get_DA(hdr)); + ar9170_print_txheader(ar, skb); +#endif /* AR9170_QUEUE_DEBUG */ + continue; + } + + r = (le32_to_cpu(txc->phy_control) & AR9170_TX_PHY_MCS_MASK) >> + AR9170_TX_PHY_MCS_SHIFT; + + if ((rate != AR9170_TX_INVALID_RATE) && (r != rate)) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: skip frame => rate %d != %d\n", + wiphy_name(ar->hw->wiphy), rate, r); + ar9170_print_txheader(ar, skb); +#endif /* AR9170_QUEUE_DEBUG */ + continue; + } + + __skb_unlink(skb, queue); + spin_unlock_irqrestore(&queue->lock, flags); + return skb; + } + +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_ERR "%s: ESS:[%pM] does not have any " + "outstanding frames in queue.\n", + wiphy_name(ar->hw->wiphy), mac); + __ar9170_dump_txqueue(ar, queue); +#endif /* AR9170_QUEUE_DEBUG */ + spin_unlock_irqrestore(&queue->lock, flags); + + return NULL; +} + +/* + * This worker tries to keeps an maintain tx_status queues. + * So we can guarantee that incoming tx_status reports are + * actually for a pending frame. + */ + +static void ar9170_tx_janitor(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, + tx_janitor.work); + struct sk_buff_head waste; + unsigned int i; + bool resched = false; + + if (unlikely(!IS_STARTED(ar))) + return ; + + skb_queue_head_init(&waste); + + for (i = 0; i < __AR9170_NUM_TXQ; i++) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: garbage collector scans queue:%d\n", + wiphy_name(ar->hw->wiphy), i); + ar9170_dump_txqueue(ar, &ar->tx_pending[i]); + ar9170_dump_txqueue(ar, &ar->tx_status[i]); +#endif /* AR9170_QUEUE_DEBUG */ + + ar9170_recycle_expired(ar, &ar->tx_status[i], &waste); + ar9170_recycle_expired(ar, &ar->tx_pending[i], &waste); + skb_queue_purge(&waste); + + if (!skb_queue_empty(&ar->tx_status[i]) || + !skb_queue_empty(&ar->tx_pending[i])) + resched = true; + } + + if (resched) + queue_delayed_work(ar->hw->workqueue, + &ar->tx_janitor, + msecs_to_jiffies(AR9170_JANITOR_DELAY)); +} + +void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) +{ + struct ar9170_cmd_response *cmd = (void *) buf; + + if ((cmd->type & 0xc0) != 0xc0) { + ar->callback_cmd(ar, len, buf); + return; + } + + /* hardware event handlers */ + switch (cmd->type) { + case 0xc1: { + /* + * TX status notification: + * bytes: 0c c1 XX YY M1 M2 M3 M4 M5 M6 R4 R3 R2 R1 S2 S1 + * + * XX always 81 + * YY always 00 + * M1-M6 is the MAC address + * R1-R4 is the transmit rate + * S1-S2 is the transmit status + */ + + struct sk_buff *skb; + u32 phy = le32_to_cpu(cmd->tx_status.rate); + u32 q = (phy & AR9170_TX_PHY_QOS_MASK) >> + AR9170_TX_PHY_QOS_SHIFT; +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: recv tx_status for %pM, p:%08x, q:%d\n", + wiphy_name(ar->hw->wiphy), cmd->tx_status.dst, phy, q); +#endif /* AR9170_QUEUE_DEBUG */ + + skb = ar9170_get_queued_skb(ar, cmd->tx_status.dst, + &ar->tx_status[q], + AR9170_TX_INVALID_RATE); + if (unlikely(!skb)) + return ; + + ar9170_tx_status(ar, skb, le16_to_cpu(cmd->tx_status.status)); + break; + } + + case 0xc0: + /* + * pre-TBTT event + */ + if (ar->vif && ar->vif->type == NL80211_IFTYPE_AP) + queue_work(ar->hw->workqueue, &ar->beacon_work); + break; + + case 0xc2: + /* + * (IBSS) beacon send notification + * bytes: 04 c2 XX YY B4 B3 B2 B1 + * + * XX always 80 + * YY always 00 + * B1-B4 "should" be the number of send out beacons. + */ + break; + + case 0xc3: + /* End of Atim Window */ + break; + + case 0xc4: + case 0xc5: + /* BlockACK events */ + break; + + case 0xc6: + /* Watchdog Interrupt */ + break; + + case 0xc9: + /* retransmission issue / SIFS/EIFS collision ?! */ + break; + + /* firmware debug */ + case 0xca: + printk(KERN_DEBUG "ar9170 FW: %.*s\n", len - 4, (char *)buf + 4); + break; + case 0xcb: + len -= 4; + + switch (len) { + case 1: + printk(KERN_DEBUG "ar9170 FW: u8: %#.2x\n", + *((char *)buf + 4)); + break; + case 2: + printk(KERN_DEBUG "ar9170 FW: u8: %#.4x\n", + le16_to_cpup((__le16 *)((char *)buf + 4))); + break; + case 4: + printk(KERN_DEBUG "ar9170 FW: u8: %#.8x\n", + le32_to_cpup((__le32 *)((char *)buf + 4))); + break; + case 8: + printk(KERN_DEBUG "ar9170 FW: u8: %#.16lx\n", + (unsigned long)le64_to_cpup( + (__le64 *)((char *)buf + 4))); + break; + } + break; + case 0xcc: + print_hex_dump_bytes("ar9170 FW:", DUMP_PREFIX_NONE, + (char *)buf + 4, len - 4); + break; + + default: + printk(KERN_INFO "received unhandled event %x\n", cmd->type); + print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len); + break; + } +} + +static void ar9170_rx_reset_rx_mpdu(struct ar9170 *ar) +{ + memset(&ar->rx_mpdu.plcp, 0, sizeof(struct ar9170_rx_head)); + ar->rx_mpdu.has_plcp = false; +} + +int ar9170_nag_limiter(struct ar9170 *ar) +{ + bool print_message; + + /* + * we expect all sorts of errors in promiscuous mode. + * don't bother with it, it's OK! + */ + if (ar->sniffer_enabled) + return false; + + /* + * only go for frequent errors! The hardware tends to + * do some stupid thing once in a while under load, in + * noisy environments or just for fun! + */ + if (time_before(jiffies, ar->bad_hw_nagger) && net_ratelimit()) + print_message = true; + else + print_message = false; + + /* reset threshold for "once in a while" */ + ar->bad_hw_nagger = jiffies + HZ / 4; + return print_message; +} + +static int ar9170_rx_mac_status(struct ar9170 *ar, + struct ar9170_rx_head *head, + struct ar9170_rx_macstatus *mac, + struct ieee80211_rx_status *status) +{ + u8 error, decrypt; + + BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12); + BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4); + + error = mac->error; + if (error & AR9170_RX_ERROR_MMIC) { + status->flag |= RX_FLAG_MMIC_ERROR; + error &= ~AR9170_RX_ERROR_MMIC; + } + + if (error & AR9170_RX_ERROR_PLCP) { + status->flag |= RX_FLAG_FAILED_PLCP_CRC; + error &= ~AR9170_RX_ERROR_PLCP; + + if (!(ar->filter_state & FIF_PLCPFAIL)) + return -EINVAL; + } + + if (error & AR9170_RX_ERROR_FCS) { + status->flag |= RX_FLAG_FAILED_FCS_CRC; + error &= ~AR9170_RX_ERROR_FCS; + + if (!(ar->filter_state & FIF_FCSFAIL)) + return -EINVAL; + } + + decrypt = ar9170_get_decrypt_type(mac); + if (!(decrypt & AR9170_RX_ENC_SOFTWARE) && + decrypt != AR9170_ENC_ALG_NONE) + status->flag |= RX_FLAG_DECRYPTED; + + /* ignore wrong RA errors */ + error &= ~AR9170_RX_ERROR_WRONG_RA; + + if (error & AR9170_RX_ERROR_DECRYPT) { + error &= ~AR9170_RX_ERROR_DECRYPT; + /* + * Rx decryption is done in place, + * the original data is lost anyway. + */ + + return -EINVAL; + } + + /* drop any other error frames */ + if (unlikely(error)) { + /* TODO: update netdevice's RX dropped/errors statistics */ + + if (ar9170_nag_limiter(ar)) + printk(KERN_DEBUG "%s: received frame with " + "suspicious error code (%#x).\n", + wiphy_name(ar->hw->wiphy), error); + + return -EINVAL; + } + + status->band = ar->channel->band; + status->freq = ar->channel->center_freq; + + switch (mac->status & AR9170_RX_STATUS_MODULATION_MASK) { + case AR9170_RX_STATUS_MODULATION_CCK: + if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE) + status->flag |= RX_FLAG_SHORTPRE; + switch (head->plcp[0]) { + case 0x0a: + status->rate_idx = 0; + break; + case 0x14: + status->rate_idx = 1; + break; + case 0x37: + status->rate_idx = 2; + break; + case 0x6e: + status->rate_idx = 3; + break; + default: + if (ar9170_nag_limiter(ar)) + printk(KERN_ERR "%s: invalid plcp cck rate " + "(%x).\n", wiphy_name(ar->hw->wiphy), + head->plcp[0]); + return -EINVAL; + } + break; + + case AR9170_RX_STATUS_MODULATION_OFDM: + switch (head->plcp[0] & 0xf) { + case 0xb: + status->rate_idx = 0; + break; + case 0xf: + status->rate_idx = 1; + break; + case 0xa: + status->rate_idx = 2; + break; + case 0xe: + status->rate_idx = 3; + break; + case 0x9: + status->rate_idx = 4; + break; + case 0xd: + status->rate_idx = 5; + break; + case 0x8: + status->rate_idx = 6; + break; + case 0xc: + status->rate_idx = 7; + break; + default: + if (ar9170_nag_limiter(ar)) + printk(KERN_ERR "%s: invalid plcp ofdm rate " + "(%x).\n", wiphy_name(ar->hw->wiphy), + head->plcp[0]); + return -EINVAL; + } + if (status->band == IEEE80211_BAND_2GHZ) + status->rate_idx += 4; + break; + + case AR9170_RX_STATUS_MODULATION_HT: + if (head->plcp[3] & 0x80) + status->flag |= RX_FLAG_40MHZ; + if (head->plcp[6] & 0x80) + status->flag |= RX_FLAG_SHORT_GI; + + status->rate_idx = clamp(0, 75, head->plcp[6] & 0x7f); + status->flag |= RX_FLAG_HT; + break; + + case AR9170_RX_STATUS_MODULATION_DUPOFDM: + /* XXX */ + if (ar9170_nag_limiter(ar)) + printk(KERN_ERR "%s: invalid modulation\n", + wiphy_name(ar->hw->wiphy)); + return -EINVAL; + } + + return 0; +} + +static void ar9170_rx_phy_status(struct ar9170 *ar, + struct ar9170_rx_phystatus *phy, + struct ieee80211_rx_status *status) +{ + int i; + + BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != 20); + + for (i = 0; i < 3; i++) + if (phy->rssi[i] != 0x80) + status->antenna |= BIT(i); + + /* post-process RSSI */ + for (i = 0; i < 7; i++) + if (phy->rssi[i] & 0x80) + phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f; + + /* TODO: we could do something with phy_errors */ + status->signal = ar->noise[0] + phy->rssi_combined; + status->noise = ar->noise[0]; +} + +static struct sk_buff *ar9170_rx_copy_data(u8 *buf, int len) +{ + struct sk_buff *skb; + int reserved = 0; + struct ieee80211_hdr *hdr = (void *) buf; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + reserved += NET_IP_ALIGN; + + if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) + reserved += NET_IP_ALIGN; + } + + if (ieee80211_has_a4(hdr->frame_control)) + reserved += NET_IP_ALIGN; + + reserved = 32 + (reserved & NET_IP_ALIGN); + + skb = dev_alloc_skb(len + reserved); + if (likely(skb)) { + skb_reserve(skb, reserved); + memcpy(skb_put(skb, len), buf, len); + } + + return skb; +} + +/* + * If the frame alignment is right (or the kernel has + * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there + * is only a single MPDU in the USB frame, then we could + * submit to mac80211 the SKB directly. However, since + * there may be multiple packets in one SKB in stream + * mode, and we need to observe the proper ordering, + * this is non-trivial. + */ + +static void ar9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) +{ + struct ar9170_rx_head *head; + struct ar9170_rx_macstatus *mac; + struct ar9170_rx_phystatus *phy = NULL; + struct ieee80211_rx_status status; + struct sk_buff *skb; + int mpdu_len; + + if (unlikely(!IS_STARTED(ar) || len < (sizeof(*mac)))) + return ; + + /* Received MPDU */ + mpdu_len = len - sizeof(*mac); + + mac = (void *)(buf + mpdu_len); + if (unlikely(mac->error & AR9170_RX_ERROR_FATAL)) { + /* this frame is too damaged and can't be used - drop it */ + + return ; + } + + switch (mac->status & AR9170_RX_STATUS_MPDU_MASK) { + case AR9170_RX_STATUS_MPDU_FIRST: + /* first mpdu packet has the plcp header */ + if (likely(mpdu_len >= sizeof(struct ar9170_rx_head))) { + head = (void *) buf; + memcpy(&ar->rx_mpdu.plcp, (void *) buf, + sizeof(struct ar9170_rx_head)); + + mpdu_len -= sizeof(struct ar9170_rx_head); + buf += sizeof(struct ar9170_rx_head); + ar->rx_mpdu.has_plcp = true; + } else { + if (ar9170_nag_limiter(ar)) + printk(KERN_ERR "%s: plcp info is clipped.\n", + wiphy_name(ar->hw->wiphy)); + return ; + } + break; + + case AR9170_RX_STATUS_MPDU_LAST: + /* last mpdu has a extra tail with phy status information */ + + if (likely(mpdu_len >= sizeof(struct ar9170_rx_phystatus))) { + mpdu_len -= sizeof(struct ar9170_rx_phystatus); + phy = (void *)(buf + mpdu_len); + } else { + if (ar9170_nag_limiter(ar)) + printk(KERN_ERR "%s: frame tail is clipped.\n", + wiphy_name(ar->hw->wiphy)); + return ; + } + + case AR9170_RX_STATUS_MPDU_MIDDLE: + /* middle mpdus are just data */ + if (unlikely(!ar->rx_mpdu.has_plcp)) { + if (!ar9170_nag_limiter(ar)) + return ; + + printk(KERN_ERR "%s: rx stream did not start " + "with a first_mpdu frame tag.\n", + wiphy_name(ar->hw->wiphy)); + + return ; + } + + head = &ar->rx_mpdu.plcp; + break; + + case AR9170_RX_STATUS_MPDU_SINGLE: + /* single mpdu - has plcp (head) and phy status (tail) */ + head = (void *) buf; + + mpdu_len -= sizeof(struct ar9170_rx_head); + mpdu_len -= sizeof(struct ar9170_rx_phystatus); + + buf += sizeof(struct ar9170_rx_head); + phy = (void *)(buf + mpdu_len); + break; + + default: + BUG_ON(1); + break; + } + + if (unlikely(mpdu_len < FCS_LEN)) + return ; + + memset(&status, 0, sizeof(status)); + if (unlikely(ar9170_rx_mac_status(ar, head, mac, &status))) + return ; + + if (phy) + ar9170_rx_phy_status(ar, phy, &status); + + skb = ar9170_rx_copy_data(buf, mpdu_len); + if (likely(skb)) + ieee80211_rx_irqsafe(ar->hw, skb, &status); +} + +void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb) +{ + unsigned int i, tlen, resplen, wlen = 0, clen = 0; + u8 *tbuf, *respbuf; + + tbuf = skb->data; + tlen = skb->len; + + while (tlen >= 4) { + clen = tbuf[1] << 8 | tbuf[0]; + wlen = ALIGN(clen, 4); + + /* check if this is stream has a valid tag.*/ + if (tbuf[2] != 0 || tbuf[3] != 0x4e) { + /* + * TODO: handle the highly unlikely event that the + * corrupted stream has the TAG at the right position. + */ + + /* check if the frame can be repaired. */ + if (!ar->rx_failover_missing) { + /* this is no "short read". */ + if (ar9170_nag_limiter(ar)) { + printk(KERN_ERR "%s: missing tag!\n", + wiphy_name(ar->hw->wiphy)); + goto err_telluser; + } else + goto err_silent; + } + + if (ar->rx_failover_missing > tlen) { + if (ar9170_nag_limiter(ar)) { + printk(KERN_ERR "%s: possible multi " + "stream corruption!\n", + wiphy_name(ar->hw->wiphy)); + goto err_telluser; + } else + goto err_silent; + } + + memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen); + ar->rx_failover_missing -= tlen; + + if (ar->rx_failover_missing <= 0) { + /* + * nested ar9170_rx call! + * termination is guranteed, even when the + * combined frame also have a element with + * a bad tag. + */ + + ar->rx_failover_missing = 0; + ar9170_rx(ar, ar->rx_failover); + + skb_reset_tail_pointer(ar->rx_failover); + skb_trim(ar->rx_failover, 0); + } + + return ; + } + + /* check if stream is clipped */ + if (wlen > tlen - 4) { + if (ar->rx_failover_missing) { + /* TODO: handle double stream corruption. */ + if (ar9170_nag_limiter(ar)) { + printk(KERN_ERR "%s: double rx stream " + "corruption!\n", + wiphy_name(ar->hw->wiphy)); + goto err_telluser; + } else + goto err_silent; + } + + /* + * save incomplete data set. + * the firmware will resend the missing bits when + * the rx - descriptor comes round again. + */ + + memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen); + ar->rx_failover_missing = clen - tlen; + return ; + } + resplen = clen; + respbuf = tbuf + 4; + tbuf += wlen + 4; + tlen -= wlen + 4; + + i = 0; + + /* weird thing, but this is the same in the original driver */ + while (resplen > 2 && i < 12 && + respbuf[0] == 0xff && respbuf[1] == 0xff) { + i += 2; + resplen -= 2; + respbuf += 2; + } + + if (resplen < 4) + continue; + + /* found the 6 * 0xffff marker? */ + if (i == 12) + ar9170_handle_command_response(ar, respbuf, resplen); + else + ar9170_handle_mpdu(ar, respbuf, clen); + } + + if (tlen) { + if (net_ratelimit()) + printk(KERN_ERR "%s: %d bytes of unprocessed " + "data left in rx stream!\n", + wiphy_name(ar->hw->wiphy), tlen); + + goto err_telluser; + } + + return ; + +err_telluser: + printk(KERN_ERR "%s: damaged RX stream data [want:%d, " + "data:%d, rx:%d, pending:%d ]\n", + wiphy_name(ar->hw->wiphy), clen, wlen, tlen, + ar->rx_failover_missing); + + if (ar->rx_failover_missing) + print_hex_dump_bytes("rxbuf:", DUMP_PREFIX_OFFSET, + ar->rx_failover->data, + ar->rx_failover->len); + + print_hex_dump_bytes("stream:", DUMP_PREFIX_OFFSET, + skb->data, skb->len); + + printk(KERN_ERR "%s: please check your hardware and cables, if " + "you see this message frequently.\n", + wiphy_name(ar->hw->wiphy)); + +err_silent: + if (ar->rx_failover_missing) { + skb_reset_tail_pointer(ar->rx_failover); + skb_trim(ar->rx_failover, 0); + ar->rx_failover_missing = 0; + } +} + +#define AR9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop) \ +do { \ + queue.aifs = ai_fs; \ + queue.cw_min = cwmin; \ + queue.cw_max = cwmax; \ + queue.txop = _txop; \ +} while (0) + +static int ar9170_op_start(struct ieee80211_hw *hw) +{ + struct ar9170 *ar = hw->priv; + int err, i; + + mutex_lock(&ar->mutex); + + ar->filter_changed = 0; + + /* reinitialize queues statistics */ + memset(&ar->tx_stats, 0, sizeof(ar->tx_stats)); + for (i = 0; i < __AR9170_NUM_TXQ; i++) + ar->tx_stats[i].limit = AR9170_TXQ_DEPTH; + + /* reset QoS defaults */ + AR9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023, 0); /* BEST EFFORT*/ + AR9170_FILL_QUEUE(ar->edcf[1], 7, 15, 1023, 0); /* BACKGROUND */ + AR9170_FILL_QUEUE(ar->edcf[2], 2, 7, 15, 94); /* VIDEO */ + AR9170_FILL_QUEUE(ar->edcf[3], 2, 3, 7, 47); /* VOICE */ + AR9170_FILL_QUEUE(ar->edcf[4], 2, 3, 7, 0); /* SPECIAL */ + + ar->bad_hw_nagger = jiffies; + + err = ar->open(ar); + if (err) + goto out; + + err = ar9170_init_mac(ar); + if (err) + goto out; + + err = ar9170_set_qos(ar); + if (err) + goto out; + + err = ar9170_init_phy(ar, IEEE80211_BAND_2GHZ); + if (err) + goto out; + + err = ar9170_init_rf(ar); + if (err) + goto out; + + /* start DMA */ + err = ar9170_write_reg(ar, 0x1c3d30, 0x100); + if (err) + goto out; + + ar->state = AR9170_STARTED; + +out: + mutex_unlock(&ar->mutex); + return err; +} + +static void ar9170_op_stop(struct ieee80211_hw *hw) +{ + struct ar9170 *ar = hw->priv; + unsigned int i; + + if (IS_STARTED(ar)) + ar->state = AR9170_IDLE; + + flush_workqueue(ar->hw->workqueue); + + cancel_delayed_work_sync(&ar->tx_janitor); + cancel_work_sync(&ar->filter_config_work); + cancel_work_sync(&ar->beacon_work); + mutex_lock(&ar->mutex); + + if (IS_ACCEPTING_CMD(ar)) { + ar9170_set_leds_state(ar, 0); + + /* stop DMA */ + ar9170_write_reg(ar, 0x1c3d30, 0); + ar->stop(ar); + } + + for (i = 0; i < __AR9170_NUM_TXQ; i++) { + skb_queue_purge(&ar->tx_pending[i]); + skb_queue_purge(&ar->tx_status[i]); + } + mutex_unlock(&ar->mutex); +} + +static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + struct ar9170_tx_control *txc; + struct ieee80211_tx_info *info; + struct ieee80211_tx_rate *txrate; + struct ar9170_tx_info *arinfo; + unsigned int queue = skb_get_queue_mapping(skb); + u16 keytype = 0; + u16 len, icv = 0; + + BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data)); + + hdr = (void *)skb->data; + info = IEEE80211_SKB_CB(skb); + len = skb->len; + + txc = (void *)skb_push(skb, sizeof(*txc)); + + if (info->control.hw_key) { + icv = info->control.hw_key->icv_len; + + switch (info->control.hw_key->alg) { + case ALG_WEP: + keytype = AR9170_TX_MAC_ENCR_RC4; + break; + case ALG_TKIP: + keytype = AR9170_TX_MAC_ENCR_RC4; + break; + case ALG_CCMP: + keytype = AR9170_TX_MAC_ENCR_AES; + break; + default: + WARN_ON(1); + goto err_out; + } + } + + /* Length */ + txc->length = cpu_to_le16(len + icv + 4); + + txc->mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION | + AR9170_TX_MAC_BACKOFF); + txc->mac_control |= cpu_to_le16(ar9170_qos_hwmap[queue] << + AR9170_TX_MAC_QOS_SHIFT); + txc->mac_control |= cpu_to_le16(keytype); + txc->phy_control = cpu_to_le32(0); + + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_NO_ACK); + + txrate = &info->control.rates[0]; + if (txrate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS); + else if (txrate->flags & IEEE80211_TX_RC_USE_RTS_CTS) + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS); + + arinfo = (void *)info->rate_driver_data; + arinfo->timeout = jiffies + msecs_to_jiffies(AR9170_QUEUE_TIMEOUT); + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && + (is_valid_ether_addr(ieee80211_get_DA(hdr)))) { + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + if (unlikely(!info->control.sta)) + goto err_out; + + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR); + arinfo->flags = AR9170_TX_FLAG_BLOCK_ACK; + goto out; + } + + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE); + /* + * WARNING: + * Putting the QoS queue bits into an unexplored territory is + * certainly not elegant. + * + * In my defense: This idea provides a reasonable way to + * smuggle valuable information to the tx_status callback. + * Also, the idea behind this bit-abuse came straight from + * the original driver code. + */ + + txc->phy_control |= + cpu_to_le32(queue << AR9170_TX_PHY_QOS_SHIFT); + arinfo->flags = AR9170_TX_FLAG_WAIT_FOR_ACK; + } else { + arinfo->flags = AR9170_TX_FLAG_NO_ACK; + } + +out: + return 0; + +err_out: + skb_pull(skb, sizeof(*txc)); + return -EINVAL; +} + +static void ar9170_tx_prepare_phy(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ar9170_tx_control *txc; + struct ieee80211_tx_info *info; + struct ieee80211_rate *rate = NULL; + struct ieee80211_tx_rate *txrate; + u32 power, chains; + + txc = (void *) skb->data; + info = IEEE80211_SKB_CB(skb); + txrate = &info->control.rates[0]; + + if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD) + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD); + + if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE); + + if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ); + /* this works because 40 MHz is 2 and dup is 3 */ + if (txrate->flags & IEEE80211_TX_RC_DUP_DATA) + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ_DUP); + + if (txrate->flags & IEEE80211_TX_RC_SHORT_GI) + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI); + + if (txrate->flags & IEEE80211_TX_RC_MCS) { + u32 r = txrate->idx; + u8 *txpower; + + /* heavy clip control */ + txc->phy_control |= cpu_to_le32((r & 0x7) << 7); + + r <<= AR9170_TX_PHY_MCS_SHIFT; + BUG_ON(r & ~AR9170_TX_PHY_MCS_MASK); + + txc->phy_control |= cpu_to_le32(r & AR9170_TX_PHY_MCS_MASK); + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_MOD_HT); + + if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { + if (info->band == IEEE80211_BAND_5GHZ) + txpower = ar->power_5G_ht40; + else + txpower = ar->power_2G_ht40; + } else { + if (info->band == IEEE80211_BAND_5GHZ) + txpower = ar->power_5G_ht20; + else + txpower = ar->power_2G_ht20; + } + + power = txpower[(txrate->idx) & 7]; + } else { + u8 *txpower; + u32 mod; + u32 phyrate; + u8 idx = txrate->idx; + + if (info->band != IEEE80211_BAND_2GHZ) { + idx += 4; + txpower = ar->power_5G_leg; + mod = AR9170_TX_PHY_MOD_OFDM; + } else { + if (idx < 4) { + txpower = ar->power_2G_cck; + mod = AR9170_TX_PHY_MOD_CCK; + } else { + mod = AR9170_TX_PHY_MOD_OFDM; + txpower = ar->power_2G_ofdm; + } + } + + rate = &__ar9170_ratetable[idx]; + + phyrate = rate->hw_value & 0xF; + power = txpower[(rate->hw_value & 0x30) >> 4]; + phyrate <<= AR9170_TX_PHY_MCS_SHIFT; + + txc->phy_control |= cpu_to_le32(mod); + txc->phy_control |= cpu_to_le32(phyrate); + } + + power <<= AR9170_TX_PHY_TX_PWR_SHIFT; + power &= AR9170_TX_PHY_TX_PWR_MASK; + txc->phy_control |= cpu_to_le32(power); + + /* set TX chains */ + if (ar->eeprom.tx_mask == 1) { + chains = AR9170_TX_PHY_TXCHAIN_1; + } else { + chains = AR9170_TX_PHY_TXCHAIN_2; + + /* >= 36M legacy OFDM - use only one chain */ + if (rate && rate->bitrate >= 360) + chains = AR9170_TX_PHY_TXCHAIN_1; + } + txc->phy_control |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_SHIFT); +} + +static void ar9170_tx(struct ar9170 *ar) +{ + struct sk_buff *skb; + unsigned long flags; + struct ieee80211_tx_info *info; + struct ar9170_tx_info *arinfo; + unsigned int i, frames, frames_failed, remaining_space; + int err; + bool schedule_garbagecollector = false; + + BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data)); + + if (unlikely(!IS_STARTED(ar))) + return ; + + remaining_space = AR9170_TX_MAX_PENDING; + + for (i = 0; i < __AR9170_NUM_TXQ; i++) { + spin_lock_irqsave(&ar->tx_stats_lock, flags); + if (ar->tx_stats[i].len >= ar->tx_stats[i].limit) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: queue %d full\n", + wiphy_name(ar->hw->wiphy), i); + + __ar9170_dump_txstats(ar); + printk(KERN_DEBUG "stuck frames: ===> \n"); + ar9170_dump_txqueue(ar, &ar->tx_pending[i]); + ar9170_dump_txqueue(ar, &ar->tx_status[i]); +#endif /* AR9170_QUEUE_DEBUG */ + ieee80211_stop_queue(ar->hw, i); + spin_unlock_irqrestore(&ar->tx_stats_lock, flags); + continue; + } + + frames = min(ar->tx_stats[i].limit - ar->tx_stats[i].len, + skb_queue_len(&ar->tx_pending[i])); + + if (remaining_space < frames) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: tx quota reached queue:%d, " + "remaining slots:%d, needed:%d\n", + wiphy_name(ar->hw->wiphy), i, remaining_space, + frames); + + ar9170_dump_txstats(ar); +#endif /* AR9170_QUEUE_DEBUG */ + frames = remaining_space; + } + + ar->tx_stats[i].len += frames; + ar->tx_stats[i].count += frames; + spin_unlock_irqrestore(&ar->tx_stats_lock, flags); + + if (!frames) + continue; + + frames_failed = 0; + while (frames) { + skb = skb_dequeue(&ar->tx_pending[i]); + if (unlikely(!skb)) { + frames_failed += frames; + frames = 0; + break; + } + + info = IEEE80211_SKB_CB(skb); + arinfo = (void *) info->rate_driver_data; + + /* TODO: cancel stuck frames */ + arinfo->timeout = jiffies + + msecs_to_jiffies(AR9170_TX_TIMEOUT); + +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: send frame q:%d =>\n", + wiphy_name(ar->hw->wiphy), i); + ar9170_print_txheader(ar, skb); +#endif /* AR9170_QUEUE_DEBUG */ + + err = ar->tx(ar, skb); + if (unlikely(err)) { + frames_failed++; + dev_kfree_skb_any(skb); + } else { + remaining_space--; + schedule_garbagecollector = true; + } + + frames--; + } + +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: ar9170_tx report for queue %d\n", + wiphy_name(ar->hw->wiphy), i); + + printk(KERN_DEBUG "%s: unprocessed pending frames left:\n", + wiphy_name(ar->hw->wiphy)); + ar9170_dump_txqueue(ar, &ar->tx_pending[i]); +#endif /* AR9170_QUEUE_DEBUG */ + + if (unlikely(frames_failed)) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: frames failed =>\n", + wiphy_name(ar->hw->wiphy), frames_failed); +#endif /* AR9170_QUEUE_DEBUG */ + + spin_lock_irqsave(&ar->tx_stats_lock, flags); + ar->tx_stats[i].len -= frames_failed; + ar->tx_stats[i].count -= frames_failed; + ieee80211_wake_queue(ar->hw, i); + spin_unlock_irqrestore(&ar->tx_stats_lock, flags); + } + } + + if (schedule_garbagecollector) + queue_delayed_work(ar->hw->workqueue, + &ar->tx_janitor, + msecs_to_jiffies(AR9170_JANITOR_DELAY)); +} + +int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ar9170 *ar = hw->priv; + struct ieee80211_tx_info *info; + + if (unlikely(!IS_STARTED(ar))) + goto err_free; + + if (unlikely(ar9170_tx_prepare(ar, skb))) + goto err_free; + + info = IEEE80211_SKB_CB(skb); + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + /* drop frame, we do not allow TX A-MPDU aggregation yet. */ + goto err_free; + } else { + unsigned int queue = skb_get_queue_mapping(skb); + + ar9170_tx_prepare_phy(ar, skb); + skb_queue_tail(&ar->tx_pending[queue], skb); + } + + ar9170_tx(ar); + return NETDEV_TX_OK; + +err_free: + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +static int ar9170_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ar9170 *ar = hw->priv; + int err = 0; + + mutex_lock(&ar->mutex); + + if (ar->vif) { + err = -EBUSY; + goto unlock; + } + + ar->vif = conf->vif; + memcpy(ar->mac_addr, conf->mac_addr, ETH_ALEN); + + if (modparam_nohwcrypt || (ar->vif->type != NL80211_IFTYPE_STATION)) { + ar->rx_software_decryption = true; + ar->disable_offload = true; + } + + ar->cur_filter = 0; + ar->want_filter = AR9170_MAC_REG_FTF_DEFAULTS; + err = ar9170_update_frame_filter(ar); + if (err) + goto unlock; + + err = ar9170_set_operating_mode(ar); + +unlock: + mutex_unlock(&ar->mutex); + return err; +} + +static void ar9170_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ar9170 *ar = hw->priv; + + mutex_lock(&ar->mutex); + ar->vif = NULL; + ar->want_filter = 0; + ar9170_update_frame_filter(ar); + ar9170_set_beacon_timers(ar); + dev_kfree_skb(ar->beacon); + ar->beacon = NULL; + ar->sniffer_enabled = false; + ar->rx_software_decryption = false; + ar9170_set_operating_mode(ar); + mutex_unlock(&ar->mutex); +} + +static int ar9170_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ar9170 *ar = hw->priv; + int err = 0; + + mutex_lock(&ar->mutex); + + if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { + /* TODO */ + err = 0; + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + /* TODO */ + err = 0; + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + /* TODO */ + err = 0; + } + + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { + /* + * is it long_frame_max_tx_count or short_frame_max_tx_count? + */ + + err = ar9170_set_hwretry_limit(ar, + ar->hw->conf.long_frame_max_tx_count); + if (err) + goto out; + } + + if (changed & BSS_CHANGED_BEACON_INT) { + err = ar9170_set_beacon_timers(ar); + if (err) + goto out; + } + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + + /* adjust slot time for 5 GHz */ + err = ar9170_set_slot_time(ar); + if (err) + goto out; + + err = ar9170_set_dyn_sifs_ack(ar); + if (err) + goto out; + + err = ar9170_set_channel(ar, hw->conf.channel, + AR9170_RFI_NONE, + nl80211_to_ar9170(hw->conf.channel_type)); + if (err) + goto out; + } + +out: + mutex_unlock(&ar->mutex); + return err; +} + +static void ar9170_set_filters(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, + filter_config_work); + int err; + + if (unlikely(!IS_STARTED(ar))) + return ; + + mutex_lock(&ar->mutex); + if (test_and_clear_bit(AR9170_FILTER_CHANGED_MODE, + &ar->filter_changed)) { + err = ar9170_set_operating_mode(ar); + if (err) + goto unlock; + } + + if (test_and_clear_bit(AR9170_FILTER_CHANGED_MULTICAST, + &ar->filter_changed)) { + err = ar9170_update_multicast(ar); + if (err) + goto unlock; + } + + if (test_and_clear_bit(AR9170_FILTER_CHANGED_FRAMEFILTER, + &ar->filter_changed)) { + err = ar9170_update_frame_filter(ar); + if (err) + goto unlock; + } + +unlock: + mutex_unlock(&ar->mutex); +} + +static void ar9170_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *new_flags, + int mc_count, struct dev_mc_list *mclist) +{ + struct ar9170 *ar = hw->priv; + + /* mask supported flags */ + *new_flags &= FIF_ALLMULTI | FIF_CONTROL | FIF_BCN_PRBRESP_PROMISC | + FIF_PROMISC_IN_BSS | FIF_FCSFAIL | FIF_PLCPFAIL; + ar->filter_state = *new_flags; + /* + * We can support more by setting the sniffer bit and + * then checking the error flags, later. + */ + + if (changed_flags & FIF_ALLMULTI) { + if (*new_flags & FIF_ALLMULTI) { + ar->want_mc_hash = ~0ULL; + } else { + u64 mchash; + int i; + + /* always get broadcast frames */ + mchash = 1ULL << (0xff >> 2); + + for (i = 0; i < mc_count; i++) { + if (WARN_ON(!mclist)) + break; + mchash |= 1ULL << (mclist->dmi_addr[5] >> 2); + mclist = mclist->next; + } + ar->want_mc_hash = mchash; + } + set_bit(AR9170_FILTER_CHANGED_MULTICAST, &ar->filter_changed); + } + + if (changed_flags & FIF_CONTROL) { + u32 filter = AR9170_MAC_REG_FTF_PSPOLL | + AR9170_MAC_REG_FTF_RTS | + AR9170_MAC_REG_FTF_CTS | + AR9170_MAC_REG_FTF_ACK | + AR9170_MAC_REG_FTF_CFE | + AR9170_MAC_REG_FTF_CFE_ACK; + + if (*new_flags & FIF_CONTROL) + ar->want_filter = ar->cur_filter | filter; + else + ar->want_filter = ar->cur_filter & ~filter; + + set_bit(AR9170_FILTER_CHANGED_FRAMEFILTER, + &ar->filter_changed); + } + + if (changed_flags & FIF_PROMISC_IN_BSS) { + ar->sniffer_enabled = ((*new_flags) & FIF_PROMISC_IN_BSS) != 0; + set_bit(AR9170_FILTER_CHANGED_MODE, + &ar->filter_changed); + } + + if (likely(IS_STARTED(ar))) + queue_work(ar->hw->workqueue, &ar->filter_config_work); +} + +static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct ar9170 *ar = hw->priv; + int err = 0; + + mutex_lock(&ar->mutex); + + if (changed & BSS_CHANGED_BSSID) { + memcpy(ar->bssid, bss_conf->bssid, ETH_ALEN); + err = ar9170_set_operating_mode(ar); + if (err) + goto out; + } + + if (changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) { + err = ar9170_update_beacon(ar); + if (err) + goto out; + + err = ar9170_set_beacon_timers(ar); + if (err) + goto out; + } + + if (changed & BSS_CHANGED_ASSOC) { +#ifndef CONFIG_AR9170_LEDS + /* enable assoc LED. */ + err = ar9170_set_leds_state(ar, bss_conf->assoc ? 2 : 0); +#endif /* CONFIG_AR9170_LEDS */ + } + + if (changed & BSS_CHANGED_BEACON_INT) { + err = ar9170_set_beacon_timers(ar); + if (err) + goto out; + } + + if (changed & BSS_CHANGED_HT) { + /* TODO */ + err = 0; + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + err = ar9170_set_slot_time(ar); + if (err) + goto out; + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + err = ar9170_set_basic_rates(ar); + if (err) + goto out; + } + +out: + mutex_unlock(&ar->mutex); +} + +static u64 ar9170_op_get_tsf(struct ieee80211_hw *hw) +{ + struct ar9170 *ar = hw->priv; + int err; + u32 tsf_low; + u32 tsf_high; + u64 tsf; + + mutex_lock(&ar->mutex); + err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_L, &tsf_low); + if (!err) + err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_H, &tsf_high); + mutex_unlock(&ar->mutex); + + if (WARN_ON(err)) + return 0; + + tsf = tsf_high; + tsf = (tsf << 32) | tsf_low; + return tsf; +} + +static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ar9170 *ar = hw->priv; + int err = 0, i; + u8 ktype; + + if ((!ar->vif) || (ar->disable_offload)) + return -EOPNOTSUPP; + + switch (key->alg) { + case ALG_WEP: + if (key->keylen == WLAN_KEY_LEN_WEP40) + ktype = AR9170_ENC_ALG_WEP64; + else + ktype = AR9170_ENC_ALG_WEP128; + break; + case ALG_TKIP: + ktype = AR9170_ENC_ALG_TKIP; + break; + case ALG_CCMP: + ktype = AR9170_ENC_ALG_AESCCMP; + break; + default: + return -EOPNOTSUPP; + } + + mutex_lock(&ar->mutex); + if (cmd == SET_KEY) { + if (unlikely(!IS_STARTED(ar))) { + err = -EOPNOTSUPP; + goto out; + } + + /* group keys need all-zeroes address */ + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + sta = NULL; + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { + for (i = 0; i < 64; i++) + if (!(ar->usedkeys & BIT(i))) + break; + if (i == 64) { + ar->rx_software_decryption = true; + ar9170_set_operating_mode(ar); + err = -ENOSPC; + goto out; + } + } else { + i = 64 + key->keyidx; + } + + key->hw_key_idx = i; + + err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL, ktype, 0, + key->key, min_t(u8, 16, key->keylen)); + if (err) + goto out; + + if (key->alg == ALG_TKIP) { + err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL, + ktype, 1, key->key + 16, 16); + if (err) + goto out; + + /* + * hardware is not capable generating the MMIC + * for fragmented frames! + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + } + + if (i < 64) + ar->usedkeys |= BIT(i); + + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } else { + if (unlikely(!IS_STARTED(ar))) { + /* The device is gone... together with the key ;-) */ + err = 0; + goto out; + } + + err = ar9170_disable_key(ar, key->hw_key_idx); + if (err) + goto out; + + if (key->hw_key_idx < 64) { + ar->usedkeys &= ~BIT(key->hw_key_idx); + } else { + err = ar9170_upload_key(ar, key->hw_key_idx, NULL, + AR9170_ENC_ALG_NONE, 0, + NULL, 0); + if (err) + goto out; + + if (key->alg == ALG_TKIP) { + err = ar9170_upload_key(ar, key->hw_key_idx, + NULL, + AR9170_ENC_ALG_NONE, 1, + NULL, 0); + if (err) + goto out; + } + + } + } + + ar9170_regwrite_begin(ar); + ar9170_regwrite(AR9170_MAC_REG_ROLL_CALL_TBL_L, ar->usedkeys); + ar9170_regwrite(AR9170_MAC_REG_ROLL_CALL_TBL_H, ar->usedkeys >> 32); + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + +out: + mutex_unlock(&ar->mutex); + + return err; +} + +static void ar9170_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ +} + +static int ar9170_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct ar9170 *ar = hw->priv; + u32 val; + int err; + + mutex_lock(&ar->mutex); + err = ar9170_read_reg(ar, AR9170_MAC_REG_TX_RETRY, &val); + ar->stats.dot11ACKFailureCount += val; + + memcpy(stats, &ar->stats, sizeof(*stats)); + mutex_unlock(&ar->mutex); + + return 0; +} + +static int ar9170_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *tx_stats) +{ + struct ar9170 *ar = hw->priv; + + spin_lock_bh(&ar->tx_stats_lock); + memcpy(tx_stats, ar->tx_stats, sizeof(tx_stats[0]) * hw->queues); + spin_unlock_bh(&ar->tx_stats_lock); + + return 0; +} + +static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *param) +{ + struct ar9170 *ar = hw->priv; + int ret; + + mutex_lock(&ar->mutex); + if ((param) && !(queue > __AR9170_NUM_TXQ)) { + memcpy(&ar->edcf[ar9170_qos_hwmap[queue]], + param, sizeof(*param)); + + ret = ar9170_set_qos(ar); + } else + ret = -EINVAL; + + mutex_unlock(&ar->mutex); + return ret; +} + +static int ar9170_ampdu_action(struct ieee80211_hw *hw, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) +{ + switch (action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + /* + * Something goes wrong -- RX locks up + * after a while of receiving aggregated + * frames -- not enabling for now. + */ + return -EOPNOTSUPP; + default: + return -EOPNOTSUPP; + } +} + +static const struct ieee80211_ops ar9170_ops = { + .start = ar9170_op_start, + .stop = ar9170_op_stop, + .tx = ar9170_op_tx, + .add_interface = ar9170_op_add_interface, + .remove_interface = ar9170_op_remove_interface, + .config = ar9170_op_config, + .configure_filter = ar9170_op_configure_filter, + .conf_tx = ar9170_conf_tx, + .bss_info_changed = ar9170_op_bss_info_changed, + .get_tsf = ar9170_op_get_tsf, + .set_key = ar9170_set_key, + .sta_notify = ar9170_sta_notify, + .get_stats = ar9170_get_stats, + .get_tx_stats = ar9170_get_tx_stats, + .ampdu_action = ar9170_ampdu_action, +}; + +void *ar9170_alloc(size_t priv_size) +{ + struct ieee80211_hw *hw; + struct ar9170 *ar; + struct sk_buff *skb; + int i; + + /* + * this buffer is used for rx stream reconstruction. + * Under heavy load this device (or the transport layer?) + * tends to split the streams into seperate rx descriptors. + */ + + skb = __dev_alloc_skb(AR9170_MAX_RX_BUFFER_SIZE, GFP_KERNEL); + if (!skb) + goto err_nomem; + + hw = ieee80211_alloc_hw(priv_size, &ar9170_ops); + if (!hw) + goto err_nomem; + + ar = hw->priv; + ar->hw = hw; + ar->rx_failover = skb; + + mutex_init(&ar->mutex); + spin_lock_init(&ar->cmdlock); + spin_lock_init(&ar->tx_stats_lock); + for (i = 0; i < __AR9170_NUM_TXQ; i++) { + skb_queue_head_init(&ar->tx_status[i]); + skb_queue_head_init(&ar->tx_pending[i]); + } + ar9170_rx_reset_rx_mpdu(ar); + INIT_WORK(&ar->filter_config_work, ar9170_set_filters); + INIT_WORK(&ar->beacon_work, ar9170_new_beacon); + INIT_DELAYED_WORK(&ar->tx_janitor, ar9170_tx_janitor); + + /* all hw supports 2.4 GHz, so set channel to 1 by default */ + ar->channel = &ar9170_2ghz_chantable[0]; + + /* first part of wiphy init */ + ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_ADHOC); + ar->hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_NOISE_DBM; + + ar->hw->queues = __AR9170_NUM_TXQ; + ar->hw->extra_tx_headroom = 8; + ar->hw->sta_data_size = sizeof(struct ar9170_sta_info); + + ar->hw->max_rates = 1; + ar->hw->max_rate_tries = 3; + + for (i = 0; i < ARRAY_SIZE(ar->noise); i++) + ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */ + + return ar; + +err_nomem: + kfree_skb(skb); + return ERR_PTR(-ENOMEM); +} + +static int ar9170_read_eeprom(struct ar9170 *ar) +{ +#define RW 8 /* number of words to read at once */ +#define RB (sizeof(u32) * RW) + DECLARE_MAC_BUF(mbuf); + u8 *eeprom = (void *)&ar->eeprom; + u8 *addr = ar->eeprom.mac_address; + __le32 offsets[RW]; + int i, j, err, bands = 0; + + BUILD_BUG_ON(sizeof(ar->eeprom) & 3); + + BUILD_BUG_ON(RB > AR9170_MAX_CMD_LEN - 4); +#ifndef __CHECKER__ + /* don't want to handle trailing remains */ + BUILD_BUG_ON(sizeof(ar->eeprom) % RB); +#endif + + for (i = 0; i < sizeof(ar->eeprom)/RB; i++) { + for (j = 0; j < RW; j++) + offsets[j] = cpu_to_le32(AR9170_EEPROM_START + + RB * i + 4 * j); + + err = ar->exec_cmd(ar, AR9170_CMD_RREG, + RB, (u8 *) &offsets, + RB, eeprom + RB * i); + if (err) + return err; + } + +#undef RW +#undef RB + + if (ar->eeprom.length == cpu_to_le16(0xFFFF)) + return -ENODATA; + + if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) { + ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &ar9170_band_2GHz; + bands++; + } + if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) { + ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &ar9170_band_5GHz; + bands++; + } + /* + * I measured this, a bandswitch takes roughly + * 135 ms and a frequency switch about 80. + * + * FIXME: measure these values again once EEPROM settings + * are used, that will influence them! + */ + if (bands == 2) + ar->hw->channel_change_time = 135 * 1000; + else + ar->hw->channel_change_time = 80 * 1000; + + ar->regulatory.current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]); + ar->regulatory.current_rd_ext = le16_to_cpu(ar->eeprom.reg_domain[1]); + + /* second part of wiphy init */ + SET_IEEE80211_PERM_ADDR(ar->hw, addr); + + return bands ? 0 : -EINVAL; +} + +static int ar9170_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ar9170 *ar = hw->priv; + + return ath_reg_notifier_apply(wiphy, request, &ar->regulatory); +} + +int ar9170_register(struct ar9170 *ar, struct device *pdev) +{ + int err; + + /* try to read EEPROM, init MAC addr */ + err = ar9170_read_eeprom(ar); + if (err) + goto err_out; + + err = ath_regd_init(&ar->regulatory, ar->hw->wiphy, + ar9170_reg_notifier); + if (err) + goto err_out; + + err = ieee80211_register_hw(ar->hw); + if (err) + goto err_out; + + if (!ath_is_world_regd(&ar->regulatory)) + regulatory_hint(ar->hw->wiphy, ar->regulatory.alpha2); + + err = ar9170_init_leds(ar); + if (err) + goto err_unreg; + +#ifdef CONFIG_AR9170_LEDS + err = ar9170_register_leds(ar); + if (err) + goto err_unreg; +#endif /* CONFIG_AR9170_LEDS */ + + dev_info(pdev, "Atheros AR9170 is registered as '%s'\n", + wiphy_name(ar->hw->wiphy)); + + return err; + +err_unreg: + ieee80211_unregister_hw(ar->hw); + +err_out: + return err; +} + +void ar9170_unregister(struct ar9170 *ar) +{ +#ifdef CONFIG_AR9170_LEDS + ar9170_unregister_leds(ar); +#endif /* CONFIG_AR9170_LEDS */ + + kfree_skb(ar->rx_failover); + ieee80211_unregister_hw(ar->hw); + mutex_destroy(&ar->mutex); +} diff --git a/drivers/net/wireless/ath/ar9170/phy.c b/drivers/net/wireless/ath/ar9170/phy.c new file mode 100644 index 00000000000..df86f70cd81 --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/phy.c @@ -0,0 +1,1240 @@ +/* + * Atheros AR9170 driver + * + * PHY and RF code + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 <linux/bitrev.h> +#include "ar9170.h" +#include "cmd.h" + +static int ar9170_init_power_cal(struct ar9170 *ar) +{ + ar9170_regwrite_begin(ar); + + ar9170_regwrite(0x1bc000 + 0x993c, 0x7f); + ar9170_regwrite(0x1bc000 + 0x9934, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0x9938, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa234, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa238, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa38c, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa390, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa3cc, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa3d0, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa3d4, 0x3f3f3f3f); + + ar9170_regwrite_finish(); + return ar9170_regwrite_result(); +} + +struct ar9170_phy_init { + u32 reg, _5ghz_20, _5ghz_40, _2ghz_40, _2ghz_20; +}; + +static struct ar9170_phy_init ar5416_phy_init[] = { + { 0x1c5800, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c5804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, }, + { 0x1c5808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c580c, 0xad848e19, 0xad848e19, 0xad848e19, 0xad848e19, }, + { 0x1c5810, 0x7d14e000, 0x7d14e000, 0x7d14e000, 0x7d14e000, }, + { 0x1c5814, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, }, + { 0x1c5818, 0x00000090, 0x00000090, 0x00000090, 0x00000090, }, + { 0x1c581c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, }, + { 0x1c5824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, }, + { 0x1c5828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, }, + { 0x1c582c, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, }, + { 0x1c5830, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, }, + { 0x1c5838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c583c, 0x00200400, 0x00200400, 0x00200400, 0x00200400, }, + { 0x1c5840, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e, }, + { 0x1c5844, 0x1372161e, 0x13721c1e, 0x13721c24, 0x137216a4, }, + { 0x1c5848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, }, + { 0x1c584c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, }, + { 0x1c5850, 0x6c48b4e4, 0x6c48b4e4, 0x6c48b0e4, 0x6c48b0e4, }, + { 0x1c5854, 0x00000859, 0x00000859, 0x00000859, 0x00000859, }, + { 0x1c5858, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, }, + { 0x1c585c, 0x31395c5e, 0x31395c5e, 0x31395c5e, 0x31395c5e, }, + { 0x1c5860, 0x0004dd10, 0x0004dd10, 0x0004dd20, 0x0004dd20, }, + { 0x1c5868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, }, + { 0x1c586c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, }, + { 0x1c5900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5904, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5908, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c590c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, }, + { 0x1c5918, 0x00000118, 0x00000230, 0x00000268, 0x00000134, }, + { 0x1c591c, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, }, + { 0x1c5920, 0x0510081c, 0x0510081c, 0x0510001c, 0x0510001c, }, + { 0x1c5924, 0xd0058a15, 0xd0058a15, 0xd0058a15, 0xd0058a15, }, + { 0x1c5928, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c592c, 0x00000004, 0x00000004, 0x00000004, 0x00000004, }, + { 0x1c5934, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c5938, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c593c, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, }, + { 0x1c5944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, }, + { 0x1c5948, 0x9280b212, 0x9280b212, 0x9280b212, 0x9280b212, }, + { 0x1c594c, 0x00020028, 0x00020028, 0x00020028, 0x00020028, }, + { 0x1c5954, 0x5d50e188, 0x5d50e188, 0x5d50e188, 0x5d50e188, }, + { 0x1c5958, 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff, }, + { 0x1c5960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, + { 0x1c5964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, }, + { 0x1c5970, 0x190fb515, 0x190fb515, 0x190fb515, 0x190fb515, }, + { 0x1c5974, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5978, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c597c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5980, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5984, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5988, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c598c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5990, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5994, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5998, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c599c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59a4, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c59a8, 0x001fff00, 0x001fff00, 0x001fff00, 0x001fff00, }, + { 0x1c59ac, 0x006f00c4, 0x006f00c4, 0x006f00c4, 0x006f00c4, }, + { 0x1c59b0, 0x03051000, 0x03051000, 0x03051000, 0x03051000, }, + { 0x1c59b4, 0x00000820, 0x00000820, 0x00000820, 0x00000820, }, + { 0x1c59c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, }, + { 0x1c59c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, }, + { 0x1c59c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, }, + { 0x1c59cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, }, + { 0x1c59d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, }, + { 0x1c59d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59e0, 0x00000200, 0x00000200, 0x00000200, 0x00000200, }, + { 0x1c59e4, 0x64646464, 0x64646464, 0x64646464, 0x64646464, }, + { 0x1c59e8, 0x3c787878, 0x3c787878, 0x3c787878, 0x3c787878, }, + { 0x1c59ec, 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa, }, + { 0x1c59f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59fc, 0x00001042, 0x00001042, 0x00001042, 0x00001042, }, + { 0x1c5a00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5a04, 0x00000040, 0x00000040, 0x00000040, 0x00000040, }, + { 0x1c5a08, 0x00000080, 0x00000080, 0x00000080, 0x00000080, }, + { 0x1c5a0c, 0x000001a1, 0x000001a1, 0x00000141, 0x00000141, }, + { 0x1c5a10, 0x000001e1, 0x000001e1, 0x00000181, 0x00000181, }, + { 0x1c5a14, 0x00000021, 0x00000021, 0x000001c1, 0x000001c1, }, + { 0x1c5a18, 0x00000061, 0x00000061, 0x00000001, 0x00000001, }, + { 0x1c5a1c, 0x00000168, 0x00000168, 0x00000041, 0x00000041, }, + { 0x1c5a20, 0x000001a8, 0x000001a8, 0x000001a8, 0x000001a8, }, + { 0x1c5a24, 0x000001e8, 0x000001e8, 0x000001e8, 0x000001e8, }, + { 0x1c5a28, 0x00000028, 0x00000028, 0x00000028, 0x00000028, }, + { 0x1c5a2c, 0x00000068, 0x00000068, 0x00000068, 0x00000068, }, + { 0x1c5a30, 0x00000189, 0x00000189, 0x000000a8, 0x000000a8, }, + { 0x1c5a34, 0x000001c9, 0x000001c9, 0x00000169, 0x00000169, }, + { 0x1c5a38, 0x00000009, 0x00000009, 0x000001a9, 0x000001a9, }, + { 0x1c5a3c, 0x00000049, 0x00000049, 0x000001e9, 0x000001e9, }, + { 0x1c5a40, 0x00000089, 0x00000089, 0x00000029, 0x00000029, }, + { 0x1c5a44, 0x00000170, 0x00000170, 0x00000069, 0x00000069, }, + { 0x1c5a48, 0x000001b0, 0x000001b0, 0x00000190, 0x00000190, }, + { 0x1c5a4c, 0x000001f0, 0x000001f0, 0x000001d0, 0x000001d0, }, + { 0x1c5a50, 0x00000030, 0x00000030, 0x00000010, 0x00000010, }, + { 0x1c5a54, 0x00000070, 0x00000070, 0x00000050, 0x00000050, }, + { 0x1c5a58, 0x00000191, 0x00000191, 0x00000090, 0x00000090, }, + { 0x1c5a5c, 0x000001d1, 0x000001d1, 0x00000151, 0x00000151, }, + { 0x1c5a60, 0x00000011, 0x00000011, 0x00000191, 0x00000191, }, + { 0x1c5a64, 0x00000051, 0x00000051, 0x000001d1, 0x000001d1, }, + { 0x1c5a68, 0x00000091, 0x00000091, 0x00000011, 0x00000011, }, + { 0x1c5a6c, 0x000001b8, 0x000001b8, 0x00000051, 0x00000051, }, + { 0x1c5a70, 0x000001f8, 0x000001f8, 0x00000198, 0x00000198, }, + { 0x1c5a74, 0x00000038, 0x00000038, 0x000001d8, 0x000001d8, }, + { 0x1c5a78, 0x00000078, 0x00000078, 0x00000018, 0x00000018, }, + { 0x1c5a7c, 0x00000199, 0x00000199, 0x00000058, 0x00000058, }, + { 0x1c5a80, 0x000001d9, 0x000001d9, 0x00000098, 0x00000098, }, + { 0x1c5a84, 0x00000019, 0x00000019, 0x00000159, 0x00000159, }, + { 0x1c5a88, 0x00000059, 0x00000059, 0x00000199, 0x00000199, }, + { 0x1c5a8c, 0x00000099, 0x00000099, 0x000001d9, 0x000001d9, }, + { 0x1c5a90, 0x000000d9, 0x000000d9, 0x00000019, 0x00000019, }, + { 0x1c5a94, 0x000000f9, 0x000000f9, 0x00000059, 0x00000059, }, + { 0x1c5a98, 0x000000f9, 0x000000f9, 0x00000099, 0x00000099, }, + { 0x1c5a9c, 0x000000f9, 0x000000f9, 0x000000d9, 0x000000d9, }, + { 0x1c5aa0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aa4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aa8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aac, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ab0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ab4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ab8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5abc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ac0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ac4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ac8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5acc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ad0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ad4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ad8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5adc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ae0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ae4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ae8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aec, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5af0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5af4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5af8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5afc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5b00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5b04, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c5b08, 0x00000002, 0x00000002, 0x00000002, 0x00000002, }, + { 0x1c5b0c, 0x00000003, 0x00000003, 0x00000003, 0x00000003, }, + { 0x1c5b10, 0x00000004, 0x00000004, 0x00000004, 0x00000004, }, + { 0x1c5b14, 0x00000005, 0x00000005, 0x00000005, 0x00000005, }, + { 0x1c5b18, 0x00000008, 0x00000008, 0x00000008, 0x00000008, }, + { 0x1c5b1c, 0x00000009, 0x00000009, 0x00000009, 0x00000009, }, + { 0x1c5b20, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000000a, }, + { 0x1c5b24, 0x0000000b, 0x0000000b, 0x0000000b, 0x0000000b, }, + { 0x1c5b28, 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c, }, + { 0x1c5b2c, 0x0000000d, 0x0000000d, 0x0000000d, 0x0000000d, }, + { 0x1c5b30, 0x00000010, 0x00000010, 0x00000010, 0x00000010, }, + { 0x1c5b34, 0x00000011, 0x00000011, 0x00000011, 0x00000011, }, + { 0x1c5b38, 0x00000012, 0x00000012, 0x00000012, 0x00000012, }, + { 0x1c5b3c, 0x00000013, 0x00000013, 0x00000013, 0x00000013, }, + { 0x1c5b40, 0x00000014, 0x00000014, 0x00000014, 0x00000014, }, + { 0x1c5b44, 0x00000015, 0x00000015, 0x00000015, 0x00000015, }, + { 0x1c5b48, 0x00000018, 0x00000018, 0x00000018, 0x00000018, }, + { 0x1c5b4c, 0x00000019, 0x00000019, 0x00000019, 0x00000019, }, + { 0x1c5b50, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, }, + { 0x1c5b54, 0x0000001b, 0x0000001b, 0x0000001b, 0x0000001b, }, + { 0x1c5b58, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, }, + { 0x1c5b5c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, }, + { 0x1c5b60, 0x00000020, 0x00000020, 0x00000020, 0x00000020, }, + { 0x1c5b64, 0x00000021, 0x00000021, 0x00000021, 0x00000021, }, + { 0x1c5b68, 0x00000022, 0x00000022, 0x00000022, 0x00000022, }, + { 0x1c5b6c, 0x00000023, 0x00000023, 0x00000023, 0x00000023, }, + { 0x1c5b70, 0x00000024, 0x00000024, 0x00000024, 0x00000024, }, + { 0x1c5b74, 0x00000025, 0x00000025, 0x00000025, 0x00000025, }, + { 0x1c5b78, 0x00000028, 0x00000028, 0x00000028, 0x00000028, }, + { 0x1c5b7c, 0x00000029, 0x00000029, 0x00000029, 0x00000029, }, + { 0x1c5b80, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a, }, + { 0x1c5b84, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, }, + { 0x1c5b88, 0x0000002c, 0x0000002c, 0x0000002c, 0x0000002c, }, + { 0x1c5b8c, 0x0000002d, 0x0000002d, 0x0000002d, 0x0000002d, }, + { 0x1c5b90, 0x00000030, 0x00000030, 0x00000030, 0x00000030, }, + { 0x1c5b94, 0x00000031, 0x00000031, 0x00000031, 0x00000031, }, + { 0x1c5b98, 0x00000032, 0x00000032, 0x00000032, 0x00000032, }, + { 0x1c5b9c, 0x00000033, 0x00000033, 0x00000033, 0x00000033, }, + { 0x1c5ba0, 0x00000034, 0x00000034, 0x00000034, 0x00000034, }, + { 0x1c5ba4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5ba8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bac, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bb0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bb4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bb8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bbc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bc0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bc4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bc8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bcc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bd0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bd4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bd8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bdc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5be0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5be4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5be8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bec, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bf0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bf4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bf8, 0x00000010, 0x00000010, 0x00000010, 0x00000010, }, + { 0x1c5bfc, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, }, + { 0x1c5c00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c14, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c18, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c1c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c28, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c2c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c30, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c34, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c38, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c3c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cf0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cf4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cf8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cfc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6200, 0x00000008, 0x00000008, 0x0000000e, 0x0000000e, }, + { 0x1c6204, 0x00000440, 0x00000440, 0x00000440, 0x00000440, }, + { 0x1c6208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, }, + { 0x1c620c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, + { 0x1c6210, 0x40806333, 0x40806333, 0x40806333, 0x40806333, }, + { 0x1c6214, 0x00106c10, 0x00106c10, 0x00106c10, 0x00106c10, }, + { 0x1c6218, 0x009c4060, 0x009c4060, 0x009c4060, 0x009c4060, }, + { 0x1c621c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, }, + { 0x1c6220, 0x018830c6, 0x018830c6, 0x018830c6, 0x018830c6, }, + { 0x1c6224, 0x00000400, 0x00000400, 0x00000400, 0x00000400, }, + { 0x1c6228, 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5, }, + { 0x1c622c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6230, 0x00000108, 0x00000210, 0x00000210, 0x00000108, }, + { 0x1c6234, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c6238, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c623c, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, }, + { 0x1c6240, 0x38490a20, 0x38490a20, 0x38490a20, 0x38490a20, }, + { 0x1c6244, 0x00007bb6, 0x00007bb6, 0x00007bb6, 0x00007bb6, }, + { 0x1c6248, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, }, + { 0x1c624c, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c6250, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, }, + { 0x1c6254, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6258, 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380, }, + { 0x1c625c, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, }, + { 0x1c6260, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, }, + { 0x1c6264, 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11, }, + { 0x1c6268, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c626c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, + { 0x1c6274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, }, + { 0x1c6278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, + { 0x1c627c, 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce, }, + { 0x1c6300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, }, + { 0x1c6304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, }, + { 0x1c6308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, }, + { 0x1c630c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, }, + { 0x1c6310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, }, + { 0x1c6314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, }, + { 0x1c6318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, }, + { 0x1c631c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, }, + { 0x1c6320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, }, + { 0x1c6324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, }, + { 0x1c6328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, }, + { 0x1c632c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6338, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c633c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6340, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6344, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6348, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, + { 0x1c634c, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, + { 0x1c6350, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, + { 0x1c6354, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, }, + { 0x1c6358, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, }, + { 0x1c6388, 0x08000000, 0x08000000, 0x08000000, 0x08000000, }, + { 0x1c638c, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c6390, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c6394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, + { 0x1c6398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, }, + { 0x1c639c, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c63a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63a4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63a8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63ac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63b0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63b4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63b8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63bc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63c0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63c4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63c8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63cc, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c63d0, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c63d4, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c63d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, + { 0x1c63e0, 0x000000c0, 0x000000c0, 0x000000c0, 0x000000c0, }, + { 0x1c6848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, }, + { 0x1c6920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, }, + { 0x1c6960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, + { 0x1c720c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, + { 0x1c726c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, + { 0x1c7848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, }, + { 0x1c7920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, }, + { 0x1c7960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, + { 0x1c820c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, + { 0x1c826c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, +/* { 0x1c8864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, }, */ + { 0x1c8864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, }, + { 0x1c895c, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, }, + { 0x1c8968, 0x000003ce, 0x000003ce, 0x000003ce, 0x000003ce, }, + { 0x1c89bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, }, + { 0x1c9270, 0x00820820, 0x00820820, 0x00820820, 0x00820820, }, + { 0x1c935c, 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f, }, + { 0x1c9360, 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207, }, + { 0x1c9364, 0x17601685, 0x17601685, 0x17601685, 0x17601685, }, + { 0x1c9368, 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104, }, + { 0x1c936c, 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03, }, + { 0x1c9370, 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883, }, + { 0x1c9374, 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803, }, + { 0x1c9378, 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682, }, + { 0x1c937c, 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482, }, + { 0x1c9380, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, }, + { 0x1c9384, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, } +}; + +int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band) +{ + int i, err; + u32 val; + bool is_2ghz = band == IEEE80211_BAND_2GHZ; + bool is_40mhz = conf_is_ht40(&ar->hw->conf); + + ar9170_regwrite_begin(ar); + + for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) { + if (is_40mhz) { + if (is_2ghz) + val = ar5416_phy_init[i]._2ghz_40; + else + val = ar5416_phy_init[i]._5ghz_40; + } else { + if (is_2ghz) + val = ar5416_phy_init[i]._2ghz_20; + else + val = ar5416_phy_init[i]._5ghz_20; + } + + ar9170_regwrite(ar5416_phy_init[i].reg, val); + } + + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + if (err) + return err; + + /* XXX: use EEPROM data here! */ + + err = ar9170_init_power_cal(ar); + if (err) + return err; + + /* XXX: remove magic! */ + if (is_2ghz) + err = ar9170_write_reg(ar, 0x1d4014, 0x5163); + else + err = ar9170_write_reg(ar, 0x1d4014, 0x5143); + + return err; +} + +struct ar9170_rf_init { + u32 reg, _5ghz, _2ghz; +}; + +static struct ar9170_rf_init ar9170_rf_init[] = { + /* bank 0 */ + { 0x1c58b0, 0x1e5795e5, 0x1e5795e5}, + { 0x1c58e0, 0x02008020, 0x02008020}, + /* bank 1 */ + { 0x1c58b0, 0x02108421, 0x02108421}, + { 0x1c58ec, 0x00000008, 0x00000008}, + /* bank 2 */ + { 0x1c58b0, 0x0e73ff17, 0x0e73ff17}, + { 0x1c58e0, 0x00000420, 0x00000420}, + /* bank 3 */ + { 0x1c58f0, 0x01400018, 0x01c00018}, + /* bank 4 */ + { 0x1c58b0, 0x000001a1, 0x000001a1}, + { 0x1c58e8, 0x00000001, 0x00000001}, + /* bank 5 */ + { 0x1c58b0, 0x00000013, 0x00000013}, + { 0x1c58e4, 0x00000002, 0x00000002}, + /* bank 6 */ + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00004000, 0x00004000}, + { 0x1c58b0, 0x00006c00, 0x00006c00}, + { 0x1c58b0, 0x00002c00, 0x00002c00}, + { 0x1c58b0, 0x00004800, 0x00004800}, + { 0x1c58b0, 0x00004000, 0x00004000}, + { 0x1c58b0, 0x00006000, 0x00006000}, + { 0x1c58b0, 0x00001000, 0x00001000}, + { 0x1c58b0, 0x00004000, 0x00004000}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00087c00, 0x00087c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00005400, 0x00005400}, + { 0x1c58b0, 0x00000c00, 0x00000c00}, + { 0x1c58b0, 0x00001800, 0x00001800}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00006c00, 0x00006c00}, + { 0x1c58b0, 0x00006c00, 0x00006c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00002c00, 0x00002c00}, + { 0x1c58b0, 0x00003c00, 0x00003c00}, + { 0x1c58b0, 0x00003800, 0x00003800}, + { 0x1c58b0, 0x00001c00, 0x00001c00}, + { 0x1c58b0, 0x00000800, 0x00000800}, + { 0x1c58b0, 0x00000408, 0x00000408}, + { 0x1c58b0, 0x00004c15, 0x00004c15}, + { 0x1c58b0, 0x00004188, 0x00004188}, + { 0x1c58b0, 0x0000201e, 0x0000201e}, + { 0x1c58b0, 0x00010408, 0x00010408}, + { 0x1c58b0, 0x00000801, 0x00000801}, + { 0x1c58b0, 0x00000c08, 0x00000c08}, + { 0x1c58b0, 0x0000181e, 0x0000181e}, + { 0x1c58b0, 0x00001016, 0x00001016}, + { 0x1c58b0, 0x00002800, 0x00002800}, + { 0x1c58b0, 0x00004010, 0x00004010}, + { 0x1c58b0, 0x0000081c, 0x0000081c}, + { 0x1c58b0, 0x00000115, 0x00000115}, + { 0x1c58b0, 0x00000015, 0x00000015}, + { 0x1c58b0, 0x00000066, 0x00000066}, + { 0x1c58b0, 0x0000001c, 0x0000001c}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000004, 0x00000004}, + { 0x1c58b0, 0x00000015, 0x00000015}, + { 0x1c58b0, 0x0000001f, 0x0000001f}, + { 0x1c58e0, 0x00000000, 0x00000400}, + /* bank 7 */ + { 0x1c58b0, 0x000000a0, 0x000000a0}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000040, 0x00000040}, + { 0x1c58f0, 0x0000001c, 0x0000001c}, +}; + +static int ar9170_init_rf_banks_0_7(struct ar9170 *ar, bool band5ghz) +{ + int err, i; + + ar9170_regwrite_begin(ar); + + for (i = 0; i < ARRAY_SIZE(ar9170_rf_init); i++) + ar9170_regwrite(ar9170_rf_init[i].reg, + band5ghz ? ar9170_rf_init[i]._5ghz + : ar9170_rf_init[i]._2ghz); + + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + if (err) + printk(KERN_ERR "%s: rf init failed\n", + wiphy_name(ar->hw->wiphy)); + return err; +} + +static int ar9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz, + u32 freq, enum ar9170_bw bw) +{ + int err; + u32 d0, d1, td0, td1, fd0, fd1; + u8 chansel; + u8 refsel0 = 1, refsel1 = 0; + u8 lf_synth = 0; + + switch (bw) { + case AR9170_BW_40_ABOVE: + freq += 10; + break; + case AR9170_BW_40_BELOW: + freq -= 10; + break; + case AR9170_BW_20: + break; + case __AR9170_NUM_BW: + BUG(); + } + + if (band5ghz) { + if (freq % 10) { + chansel = (freq - 4800) / 5; + } else { + chansel = ((freq - 4800) / 10) * 2; + refsel0 = 0; + refsel1 = 1; + } + chansel = byte_rev_table[chansel]; + } else { + if (freq == 2484) { + chansel = 10 + (freq - 2274) / 5; + lf_synth = 1; + } else + chansel = 16 + (freq - 2272) / 5; + chansel *= 4; + chansel = byte_rev_table[chansel]; + } + + d1 = chansel; + d0 = 0x21 | + refsel0 << 3 | + refsel1 << 2 | + lf_synth << 1; + td0 = d0 & 0x1f; + td1 = d1 & 0x1f; + fd0 = td1 << 5 | td0; + + td0 = (d0 >> 5) & 0x7; + td1 = (d1 >> 5) & 0x7; + fd1 = td1 << 5 | td0; + + ar9170_regwrite_begin(ar); + + ar9170_regwrite(0x1c58b0, fd0); + ar9170_regwrite(0x1c58e8, fd1); + + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + if (err) + return err; + + msleep(10); + + return 0; +} + +struct ar9170_phy_freq_params { + u8 coeff_exp; + u16 coeff_man; + u8 coeff_exp_shgi; + u16 coeff_man_shgi; +}; + +struct ar9170_phy_freq_entry { + u16 freq; + struct ar9170_phy_freq_params params[__AR9170_NUM_BW]; +}; + +/* NB: must be in sync with channel tables in main! */ +static const struct ar9170_phy_freq_entry ar9170_phy_freq_params[] = { +/* + * freq, + * 20MHz, + * 40MHz (below), + * 40Mhz (above), + */ + { 2412, { + { 3, 21737, 3, 19563, }, + { 3, 21827, 3, 19644, }, + { 3, 21647, 3, 19482, }, + } }, + { 2417, { + { 3, 21692, 3, 19523, }, + { 3, 21782, 3, 19604, }, + { 3, 21602, 3, 19442, }, + } }, + { 2422, { + { 3, 21647, 3, 19482, }, + { 3, 21737, 3, 19563, }, + { 3, 21558, 3, 19402, }, + } }, + { 2427, { + { 3, 21602, 3, 19442, }, + { 3, 21692, 3, 19523, }, + { 3, 21514, 3, 19362, }, + } }, + { 2432, { + { 3, 21558, 3, 19402, }, + { 3, 21647, 3, 19482, }, + { 3, 21470, 3, 19323, }, + } }, + { 2437, { + { 3, 21514, 3, 19362, }, + { 3, 21602, 3, 19442, }, + { 3, 21426, 3, 19283, }, + } }, + { 2442, { + { 3, 21470, 3, 19323, }, + { 3, 21558, 3, 19402, }, + { 3, 21382, 3, 19244, }, + } }, + { 2447, { + { 3, 21426, 3, 19283, }, + { 3, 21514, 3, 19362, }, + { 3, 21339, 3, 19205, }, + } }, + { 2452, { + { 3, 21382, 3, 19244, }, + { 3, 21470, 3, 19323, }, + { 3, 21295, 3, 19166, }, + } }, + { 2457, { + { 3, 21339, 3, 19205, }, + { 3, 21426, 3, 19283, }, + { 3, 21252, 3, 19127, }, + } }, + { 2462, { + { 3, 21295, 3, 19166, }, + { 3, 21382, 3, 19244, }, + { 3, 21209, 3, 19088, }, + } }, + { 2467, { + { 3, 21252, 3, 19127, }, + { 3, 21339, 3, 19205, }, + { 3, 21166, 3, 19050, }, + } }, + { 2472, { + { 3, 21209, 3, 19088, }, + { 3, 21295, 3, 19166, }, + { 3, 21124, 3, 19011, }, + } }, + { 2484, { + { 3, 21107, 3, 18996, }, + { 3, 21192, 3, 19073, }, + { 3, 21022, 3, 18920, }, + } }, + { 4920, { + { 4, 21313, 4, 19181, }, + { 4, 21356, 4, 19220, }, + { 4, 21269, 4, 19142, }, + } }, + { 4940, { + { 4, 21226, 4, 19104, }, + { 4, 21269, 4, 19142, }, + { 4, 21183, 4, 19065, }, + } }, + { 4960, { + { 4, 21141, 4, 19027, }, + { 4, 21183, 4, 19065, }, + { 4, 21098, 4, 18988, }, + } }, + { 4980, { + { 4, 21056, 4, 18950, }, + { 4, 21098, 4, 18988, }, + { 4, 21014, 4, 18912, }, + } }, + { 5040, { + { 4, 20805, 4, 18725, }, + { 4, 20846, 4, 18762, }, + { 4, 20764, 4, 18687, }, + } }, + { 5060, { + { 4, 20723, 4, 18651, }, + { 4, 20764, 4, 18687, }, + { 4, 20682, 4, 18614, }, + } }, + { 5080, { + { 4, 20641, 4, 18577, }, + { 4, 20682, 4, 18614, }, + { 4, 20601, 4, 18541, }, + } }, + { 5180, { + { 4, 20243, 4, 18219, }, + { 4, 20282, 4, 18254, }, + { 4, 20204, 4, 18183, }, + } }, + { 5200, { + { 4, 20165, 4, 18148, }, + { 4, 20204, 4, 18183, }, + { 4, 20126, 4, 18114, }, + } }, + { 5220, { + { 4, 20088, 4, 18079, }, + { 4, 20126, 4, 18114, }, + { 4, 20049, 4, 18044, }, + } }, + { 5240, { + { 4, 20011, 4, 18010, }, + { 4, 20049, 4, 18044, }, + { 4, 19973, 4, 17976, }, + } }, + { 5260, { + { 4, 19935, 4, 17941, }, + { 4, 19973, 4, 17976, }, + { 4, 19897, 4, 17907, }, + } }, + { 5280, { + { 4, 19859, 4, 17873, }, + { 4, 19897, 4, 17907, }, + { 4, 19822, 4, 17840, }, + } }, + { 5300, { + { 4, 19784, 4, 17806, }, + { 4, 19822, 4, 17840, }, + { 4, 19747, 4, 17772, }, + } }, + { 5320, { + { 4, 19710, 4, 17739, }, + { 4, 19747, 4, 17772, }, + { 4, 19673, 4, 17706, }, + } }, + { 5500, { + { 4, 19065, 4, 17159, }, + { 4, 19100, 4, 17190, }, + { 4, 19030, 4, 17127, }, + } }, + { 5520, { + { 4, 18996, 4, 17096, }, + { 4, 19030, 4, 17127, }, + { 4, 18962, 4, 17065, }, + } }, + { 5540, { + { 4, 18927, 4, 17035, }, + { 4, 18962, 4, 17065, }, + { 4, 18893, 4, 17004, }, + } }, + { 5560, { + { 4, 18859, 4, 16973, }, + { 4, 18893, 4, 17004, }, + { 4, 18825, 4, 16943, }, + } }, + { 5580, { + { 4, 18792, 4, 16913, }, + { 4, 18825, 4, 16943, }, + { 4, 18758, 4, 16882, }, + } }, + { 5600, { + { 4, 18725, 4, 16852, }, + { 4, 18758, 4, 16882, }, + { 4, 18691, 4, 16822, }, + } }, + { 5620, { + { 4, 18658, 4, 16792, }, + { 4, 18691, 4, 16822, }, + { 4, 18625, 4, 16762, }, + } }, + { 5640, { + { 4, 18592, 4, 16733, }, + { 4, 18625, 4, 16762, }, + { 4, 18559, 4, 16703, }, + } }, + { 5660, { + { 4, 18526, 4, 16673, }, + { 4, 18559, 4, 16703, }, + { 4, 18493, 4, 16644, }, + } }, + { 5680, { + { 4, 18461, 4, 16615, }, + { 4, 18493, 4, 16644, }, + { 4, 18428, 4, 16586, }, + } }, + { 5700, { + { 4, 18396, 4, 16556, }, + { 4, 18428, 4, 16586, }, + { 4, 18364, 4, 16527, }, + } }, + { 5745, { + { 4, 18252, 4, 16427, }, + { 4, 18284, 4, 16455, }, + { 4, 18220, 4, 16398, }, + } }, + { 5765, { + { 4, 18189, 5, 32740, }, + { 4, 18220, 4, 16398, }, + { 4, 18157, 5, 32683, }, + } }, + { 5785, { + { 4, 18126, 5, 32626, }, + { 4, 18157, 5, 32683, }, + { 4, 18094, 5, 32570, }, + } }, + { 5805, { + { 4, 18063, 5, 32514, }, + { 4, 18094, 5, 32570, }, + { 4, 18032, 5, 32458, }, + } }, + { 5825, { + { 4, 18001, 5, 32402, }, + { 4, 18032, 5, 32458, }, + { 4, 17970, 5, 32347, }, + } }, + { 5170, { + { 4, 20282, 4, 18254, }, + { 4, 20321, 4, 18289, }, + { 4, 20243, 4, 18219, }, + } }, + { 5190, { + { 4, 20204, 4, 18183, }, + { 4, 20243, 4, 18219, }, + { 4, 20165, 4, 18148, }, + } }, + { 5210, { + { 4, 20126, 4, 18114, }, + { 4, 20165, 4, 18148, }, + { 4, 20088, 4, 18079, }, + } }, + { 5230, { + { 4, 20049, 4, 18044, }, + { 4, 20088, 4, 18079, }, + { 4, 20011, 4, 18010, }, + } }, +}; + +static const struct ar9170_phy_freq_params * +ar9170_get_hw_dyn_params(struct ieee80211_channel *channel, + enum ar9170_bw bw) +{ + unsigned int chanidx = 0; + u16 freq = 2412; + + if (channel) { + chanidx = channel->hw_value; + freq = channel->center_freq; + } + + BUG_ON(chanidx >= ARRAY_SIZE(ar9170_phy_freq_params)); + + BUILD_BUG_ON(__AR9170_NUM_BW != 3); + + WARN_ON(ar9170_phy_freq_params[chanidx].freq != freq); + + return &ar9170_phy_freq_params[chanidx].params[bw]; +} + + +int ar9170_init_rf(struct ar9170 *ar) +{ + const struct ar9170_phy_freq_params *freqpar; + __le32 cmd[7]; + int err; + + err = ar9170_init_rf_banks_0_7(ar, false); + if (err) + return err; + + err = ar9170_init_rf_bank4_pwr(ar, false, 2412, AR9170_BW_20); + if (err) + return err; + + freqpar = ar9170_get_hw_dyn_params(NULL, AR9170_BW_20); + + cmd[0] = cpu_to_le32(2412 * 1000); + cmd[1] = cpu_to_le32(0); + cmd[2] = cpu_to_le32(1); + cmd[3] = cpu_to_le32(freqpar->coeff_exp); + cmd[4] = cpu_to_le32(freqpar->coeff_man); + cmd[5] = cpu_to_le32(freqpar->coeff_exp_shgi); + cmd[6] = cpu_to_le32(freqpar->coeff_man_shgi); + + /* RF_INIT echoes the command back to us */ + err = ar->exec_cmd(ar, AR9170_CMD_RF_INIT, + sizeof(cmd), (u8 *)cmd, + sizeof(cmd), (u8 *)cmd); + if (err) + return err; + + msleep(1000); + + return ar9170_echo_test(ar, 0xaabbccdd); +} + +static int ar9170_find_freq_idx(int nfreqs, u8 *freqs, u8 f) +{ + int idx = nfreqs - 2; + + while (idx >= 0) { + if (f >= freqs[idx]) + return idx; + idx--; + } + + return 0; +} + +static s32 ar9170_interpolate_s32(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) +{ + /* nothing to interpolate, it's horizontal */ + if (y2 == y1) + return y1; + + /* check if we hit one of the edges */ + if (x == x1) + return y1; + if (x == x2) + return y2; + + /* x1 == x2 is bad, hopefully == x */ + if (x2 == x1) + return y1; + + return y1 + (((y2 - y1) * (x - x1)) / (x2 - x1)); +} + +static u8 ar9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2) +{ +#define SHIFT 8 + s32 y; + + y = ar9170_interpolate_s32(x << SHIFT, + x1 << SHIFT, y1 << SHIFT, + x2 << SHIFT, y2 << SHIFT); + + /* + * XXX: unwrap this expression + * Isn't it just DIV_ROUND_UP(y, 1<<SHIFT)? + * Can we rely on the compiler to optimise away the div? + */ + return (y >> SHIFT) + ((y & (1<<(SHIFT-1))) >> (SHIFT - 1)); +#undef SHIFT +} + +static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) +{ + struct ar9170_calibration_target_power_legacy *ctpl; + struct ar9170_calibration_target_power_ht *ctph; + u8 *ctpres; + int ntargets; + int idx, i, n; + u8 ackpower, ackchains, f; + u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS]; + + if (freq < 3000) + f = freq - 2300; + else + f = (freq - 4800)/5; + + /* + * cycle through the various modes + * + * legacy modes first: 5G, 2G CCK, 2G OFDM + */ + for (i = 0; i < 3; i++) { + switch (i) { + case 0: /* 5 GHz legacy */ + ctpl = &ar->eeprom.cal_tgt_pwr_5G[0]; + ntargets = AR5416_NUM_5G_TARGET_PWRS; + ctpres = ar->power_5G_leg; + break; + case 1: /* 2.4 GHz CCK */ + ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0]; + ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS; + ctpres = ar->power_2G_cck; + break; + case 2: /* 2.4 GHz OFDM */ + ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0]; + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; + ctpres = ar->power_2G_ofdm; + break; + default: + BUG(); + } + + for (n = 0; n < ntargets; n++) { + if (ctpl[n].freq == 0xff) + break; + pwr_freqs[n] = ctpl[n].freq; + } + ntargets = n; + idx = ar9170_find_freq_idx(ntargets, pwr_freqs, f); + for (n = 0; n < 4; n++) + ctpres[n] = ar9170_interpolate_u8( + f, + ctpl[idx + 0].freq, + ctpl[idx + 0].power[n], + ctpl[idx + 1].freq, + ctpl[idx + 1].power[n]); + } + + /* + * HT modes now: 5G HT20, 5G HT40, 2G CCK, 2G OFDM, 2G HT20, 2G HT40 + */ + for (i = 0; i < 4; i++) { + switch (i) { + case 0: /* 5 GHz HT 20 */ + ctph = &ar->eeprom.cal_tgt_pwr_5G_ht20[0]; + ntargets = AR5416_NUM_5G_TARGET_PWRS; + ctpres = ar->power_5G_ht20; + break; + case 1: /* 5 GHz HT 40 */ + ctph = &ar->eeprom.cal_tgt_pwr_5G_ht40[0]; + ntargets = AR5416_NUM_5G_TARGET_PWRS; + ctpres = ar->power_5G_ht40; + break; + case 2: /* 2.4 GHz HT 20 */ + ctph = &ar->eeprom.cal_tgt_pwr_2G_ht20[0]; + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; + ctpres = ar->power_2G_ht20; + break; + case 3: /* 2.4 GHz HT 40 */ + ctph = &ar->eeprom.cal_tgt_pwr_2G_ht40[0]; + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; + ctpres = ar->power_2G_ht40; + break; + default: + BUG(); + } + + for (n = 0; n < ntargets; n++) { + if (ctph[n].freq == 0xff) + break; + pwr_freqs[n] = ctph[n].freq; + } + ntargets = n; + idx = ar9170_find_freq_idx(ntargets, pwr_freqs, f); + for (n = 0; n < 8; n++) + ctpres[n] = ar9170_interpolate_u8( + f, + ctph[idx + 0].freq, + ctph[idx + 0].power[n], + ctph[idx + 1].freq, + ctph[idx + 1].power[n]); + } + + /* set ACK/CTS TX power */ + ar9170_regwrite_begin(ar); + + if (ar->eeprom.tx_mask != 1) + ackchains = AR9170_TX_PHY_TXCHAIN_2; + else + ackchains = AR9170_TX_PHY_TXCHAIN_1; + + if (freq < 3000) + ackpower = ar->power_2G_ofdm[0] & 0x3f; + else + ackpower = ar->power_5G_leg[0] & 0x3f; + + ar9170_regwrite(0x1c3694, ackpower << 20 | ackchains << 26); + ar9170_regwrite(0x1c3bb4, ackpower << 5 | ackchains << 11 | + ackpower << 21 | ackchains << 27); + + ar9170_regwrite_finish(); + return ar9170_regwrite_result(); +} + +static int ar9170_calc_noise_dbm(u32 raw_noise) +{ + if (raw_noise & 0x100) + return ~((raw_noise & 0x0ff) >> 1); + else + return (raw_noise & 0xff) >> 1; +} + +int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, + enum ar9170_rf_init_mode rfi, enum ar9170_bw bw) +{ + const struct ar9170_phy_freq_params *freqpar; + u32 cmd, tmp, offs; + __le32 vals[8]; + int i, err; + bool bandswitch; + + /* clear BB heavy clip enable */ + err = ar9170_write_reg(ar, 0x1c59e0, 0x200); + if (err) + return err; + + /* may be NULL at first setup */ + if (ar->channel) + bandswitch = ar->channel->band != channel->band; + else + bandswitch = true; + + /* HW workaround */ + if (!ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] && + channel->center_freq <= 2417) + bandswitch = true; + + err = ar->exec_cmd(ar, AR9170_CMD_FREQ_START, 0, NULL, 0, NULL); + if (err) + return err; + + if (rfi != AR9170_RFI_NONE || bandswitch) { + u32 val = 0x400; + + if (rfi == AR9170_RFI_COLD) + val = 0x800; + + /* warm/cold reset BB/ADDA */ + err = ar9170_write_reg(ar, 0x1d4004, val); + if (err) + return err; + + err = ar9170_write_reg(ar, 0x1d4004, 0x0); + if (err) + return err; + + err = ar9170_init_phy(ar, channel->band); + if (err) + return err; + + err = ar9170_init_rf_banks_0_7(ar, + channel->band == IEEE80211_BAND_5GHZ); + if (err) + return err; + + cmd = AR9170_CMD_RF_INIT; + } else { + cmd = AR9170_CMD_FREQUENCY; + } + + err = ar9170_init_rf_bank4_pwr(ar, + channel->band == IEEE80211_BAND_5GHZ, + channel->center_freq, bw); + if (err) + return err; + + switch (bw) { + case AR9170_BW_20: + tmp = 0x240; + offs = 0; + break; + case AR9170_BW_40_BELOW: + tmp = 0x2c4; + offs = 3; + break; + case AR9170_BW_40_ABOVE: + tmp = 0x2d4; + offs = 1; + break; + default: + BUG(); + return -ENOSYS; + } + + if (ar->eeprom.tx_mask != 1) + tmp |= 0x100; + + err = ar9170_write_reg(ar, 0x1c5804, tmp); + if (err) + return err; + + err = ar9170_set_power_cal(ar, channel->center_freq, bw); + if (err) + return err; + + freqpar = ar9170_get_hw_dyn_params(channel, bw); + + vals[0] = cpu_to_le32(channel->center_freq * 1000); + vals[1] = cpu_to_le32(conf_is_ht40(&ar->hw->conf)); + vals[2] = cpu_to_le32(offs << 2 | 1); + vals[3] = cpu_to_le32(freqpar->coeff_exp); + vals[4] = cpu_to_le32(freqpar->coeff_man); + vals[5] = cpu_to_le32(freqpar->coeff_exp_shgi); + vals[6] = cpu_to_le32(freqpar->coeff_man_shgi); + vals[7] = cpu_to_le32(1000); + + err = ar->exec_cmd(ar, cmd, sizeof(vals), (u8 *)vals, + sizeof(vals), (u8 *)vals); + if (err) + return err; + + for (i = 0; i < 2; i++) { + ar->noise[i] = ar9170_calc_noise_dbm( + (le32_to_cpu(vals[2 + i]) >> 19) & 0x1ff); + + ar->noise[i + 2] = ar9170_calc_noise_dbm( + (le32_to_cpu(vals[5 + i]) >> 23) & 0x1ff); + } + + ar->channel = channel; + return 0; +} diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c new file mode 100644 index 00000000000..754b1f8d8da --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/usb.c @@ -0,0 +1,929 @@ +/* + * Atheros AR9170 driver + * + * USB - frontend + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009, Christian Lamparter <chunkeey@web.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 <linux/module.h> +#include <linux/usb.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> +#include <net/mac80211.h> +#include "ar9170.h" +#include "cmd.h" +#include "hw.h" +#include "usb.h" + +MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); +MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless"); +MODULE_FIRMWARE("ar9170.fw"); +MODULE_FIRMWARE("ar9170-1.fw"); +MODULE_FIRMWARE("ar9170-2.fw"); + +enum ar9170_requirements { + AR9170_REQ_FW1_ONLY = 1, +}; + +static struct usb_device_id ar9170_usb_ids[] = { + /* Atheros 9170 */ + { USB_DEVICE(0x0cf3, 0x9170) }, + /* Atheros TG121N */ + { USB_DEVICE(0x0cf3, 0x1001) }, + /* Cace Airpcap NX */ + { USB_DEVICE(0xcace, 0x0300) }, + /* D-Link DWA 160A */ + { USB_DEVICE(0x07d1, 0x3c10) }, + /* Netgear WNDA3100 */ + { USB_DEVICE(0x0846, 0x9010) }, + /* Netgear WN111 v2 */ + { USB_DEVICE(0x0846, 0x9001) }, + /* Zydas ZD1221 */ + { USB_DEVICE(0x0ace, 0x1221) }, + /* ZyXEL NWD271N */ + { USB_DEVICE(0x0586, 0x3417) }, + /* Z-Com UB81 BG */ + { USB_DEVICE(0x0cde, 0x0023) }, + /* Z-Com UB82 ABG */ + { USB_DEVICE(0x0cde, 0x0026) }, + /* Arcadyan WN7512 */ + { USB_DEVICE(0x083a, 0xf522) }, + /* Planex GWUS300 */ + { USB_DEVICE(0x2019, 0x5304) }, + /* IO-Data WNGDNUS2 */ + { USB_DEVICE(0x04bb, 0x093f) }, + /* AVM FRITZ!WLAN USB Stick N */ + { USB_DEVICE(0x057C, 0x8401) }, + /* AVM FRITZ!WLAN USB Stick N 2.4 */ + { USB_DEVICE(0x057C, 0x8402), .driver_info = AR9170_REQ_FW1_ONLY }, + + /* terminate */ + {} +}; +MODULE_DEVICE_TABLE(usb, ar9170_usb_ids); + +static void ar9170_usb_submit_urb(struct ar9170_usb *aru) +{ + struct urb *urb; + unsigned long flags; + int err; + + if (unlikely(!IS_STARTED(&aru->common))) + return ; + + spin_lock_irqsave(&aru->tx_urb_lock, flags); + if (aru->tx_submitted_urbs >= AR9170_NUM_TX_URBS) { + spin_unlock_irqrestore(&aru->tx_urb_lock, flags); + return ; + } + aru->tx_submitted_urbs++; + + urb = usb_get_from_anchor(&aru->tx_pending); + if (!urb) { + aru->tx_submitted_urbs--; + spin_unlock_irqrestore(&aru->tx_urb_lock, flags); + + return ; + } + spin_unlock_irqrestore(&aru->tx_urb_lock, flags); + + aru->tx_pending_urbs--; + usb_anchor_urb(urb, &aru->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + if (ar9170_nag_limiter(&aru->common)) + dev_err(&aru->udev->dev, "submit_urb failed (%d).\n", + err); + + usb_unanchor_urb(urb); + aru->tx_submitted_urbs--; + ar9170_tx_callback(&aru->common, urb->context); + } + + usb_free_urb(urb); +} + +static void ar9170_usb_tx_urb_complete_frame(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct ar9170_usb *aru = (struct ar9170_usb *) + usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); + + if (unlikely(!aru)) { + dev_kfree_skb_irq(skb); + return ; + } + + aru->tx_submitted_urbs--; + + ar9170_tx_callback(&aru->common, skb); + + ar9170_usb_submit_urb(aru); +} + +static void ar9170_usb_tx_urb_complete(struct urb *urb) +{ +} + +static void ar9170_usb_irq_completed(struct urb *urb) +{ + struct ar9170_usb *aru = urb->context; + + switch (urb->status) { + /* everything is fine */ + case 0: + break; + + /* disconnect */ + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + goto free; + + default: + goto resubmit; + } + + ar9170_handle_command_response(&aru->common, urb->transfer_buffer, + urb->actual_length); + +resubmit: + usb_anchor_urb(urb, &aru->rx_submitted); + if (usb_submit_urb(urb, GFP_ATOMIC)) { + usb_unanchor_urb(urb); + goto free; + } + + return; + +free: + usb_buffer_free(aru->udev, 64, urb->transfer_buffer, urb->transfer_dma); +} + +static void ar9170_usb_rx_completed(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct ar9170_usb *aru = (struct ar9170_usb *) + usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); + int err; + + if (!aru) + goto free; + + switch (urb->status) { + /* everything is fine */ + case 0: + break; + + /* disconnect */ + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + goto free; + + default: + goto resubmit; + } + + skb_put(skb, urb->actual_length); + ar9170_rx(&aru->common, skb); + +resubmit: + skb_reset_tail_pointer(skb); + skb_trim(skb, 0); + + usb_anchor_urb(urb, &aru->rx_submitted); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + usb_unanchor_urb(urb); + goto free; + } + + return ; + +free: + dev_kfree_skb_irq(skb); +} + +static int ar9170_usb_prep_rx_urb(struct ar9170_usb *aru, + struct urb *urb, gfp_t gfp) +{ + struct sk_buff *skb; + + skb = __dev_alloc_skb(AR9170_MAX_RX_BUFFER_SIZE + 32, gfp); + if (!skb) + return -ENOMEM; + + /* reserve some space for mac80211's radiotap */ + skb_reserve(skb, 32); + + usb_fill_bulk_urb(urb, aru->udev, + usb_rcvbulkpipe(aru->udev, AR9170_EP_RX), + skb->data, min(skb_tailroom(skb), + AR9170_MAX_RX_BUFFER_SIZE), + ar9170_usb_rx_completed, skb); + + return 0; +} + +static int ar9170_usb_alloc_rx_irq_urb(struct ar9170_usb *aru) +{ + struct urb *urb = NULL; + void *ibuf; + int err = -ENOMEM; + + /* initialize interrupt endpoint */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto out; + + ibuf = usb_buffer_alloc(aru->udev, 64, GFP_KERNEL, &urb->transfer_dma); + if (!ibuf) + goto out; + + usb_fill_int_urb(urb, aru->udev, + usb_rcvintpipe(aru->udev, AR9170_EP_IRQ), ibuf, + 64, ar9170_usb_irq_completed, aru, 1); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &aru->rx_submitted); + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_buffer_free(aru->udev, 64, urb->transfer_buffer, + urb->transfer_dma); + } + +out: + usb_free_urb(urb); + return err; +} + +static int ar9170_usb_alloc_rx_bulk_urbs(struct ar9170_usb *aru) +{ + struct urb *urb; + int i; + int err = -EINVAL; + + for (i = 0; i < AR9170_NUM_RX_URBS; i++) { + err = -ENOMEM; + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto err_out; + + err = ar9170_usb_prep_rx_urb(aru, urb, GFP_KERNEL); + if (err) { + usb_free_urb(urb); + goto err_out; + } + + usb_anchor_urb(urb, &aru->rx_submitted); + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + dev_kfree_skb_any((void *) urb->transfer_buffer); + usb_free_urb(urb); + goto err_out; + } + usb_free_urb(urb); + } + + /* the device now waiting for a firmware. */ + aru->common.state = AR9170_IDLE; + return 0; + +err_out: + + usb_kill_anchored_urbs(&aru->rx_submitted); + return err; +} + +static int ar9170_usb_flush(struct ar9170 *ar) +{ + struct ar9170_usb *aru = (void *) ar; + struct urb *urb; + int ret, err = 0; + + if (IS_STARTED(ar)) + aru->common.state = AR9170_IDLE; + + usb_wait_anchor_empty_timeout(&aru->tx_pending, + msecs_to_jiffies(800)); + while ((urb = usb_get_from_anchor(&aru->tx_pending))) { + ar9170_tx_callback(&aru->common, (void *) urb->context); + usb_free_urb(urb); + } + + /* lets wait a while until the tx - queues are dried out */ + ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted, + msecs_to_jiffies(100)); + if (ret == 0) + err = -ETIMEDOUT; + + usb_kill_anchored_urbs(&aru->tx_submitted); + + if (IS_ACCEPTING_CMD(ar)) + aru->common.state = AR9170_STARTED; + + return err; +} + +static void ar9170_usb_cancel_urbs(struct ar9170_usb *aru) +{ + int err; + + aru->common.state = AR9170_UNKNOWN_STATE; + + err = ar9170_usb_flush(&aru->common); + if (err) + dev_err(&aru->udev->dev, "stuck tx urbs!\n"); + + usb_poison_anchored_urbs(&aru->tx_submitted); + usb_poison_anchored_urbs(&aru->rx_submitted); +} + +static int ar9170_usb_exec_cmd(struct ar9170 *ar, enum ar9170_cmd cmd, + unsigned int plen, void *payload, + unsigned int outlen, void *out) +{ + struct ar9170_usb *aru = (void *) ar; + struct urb *urb = NULL; + unsigned long flags; + int err = -ENOMEM; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return -EPERM; + + if (WARN_ON(plen > AR9170_MAX_CMD_LEN - 4)) + return -EINVAL; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (unlikely(!urb)) + goto err_free; + + ar->cmdbuf[0] = cpu_to_le32(plen); + ar->cmdbuf[0] |= cpu_to_le32(cmd << 8); + /* writing multiple regs fills this buffer already */ + if (plen && payload != (u8 *)(&ar->cmdbuf[1])) + memcpy(&ar->cmdbuf[1], payload, plen); + + spin_lock_irqsave(&aru->common.cmdlock, flags); + aru->readbuf = (u8 *)out; + aru->readlen = outlen; + spin_unlock_irqrestore(&aru->common.cmdlock, flags); + + usb_fill_int_urb(urb, aru->udev, + usb_sndbulkpipe(aru->udev, AR9170_EP_CMD), + aru->common.cmdbuf, plen + 4, + ar9170_usb_tx_urb_complete, NULL, 1); + + usb_anchor_urb(urb, &aru->tx_submitted); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + usb_unanchor_urb(urb); + usb_free_urb(urb); + goto err_unbuf; + } + usb_free_urb(urb); + + err = wait_for_completion_timeout(&aru->cmd_wait, HZ); + if (err == 0) { + err = -ETIMEDOUT; + goto err_unbuf; + } + + if (aru->readlen != outlen) { + err = -EMSGSIZE; + goto err_unbuf; + } + + return 0; + +err_unbuf: + /* Maybe the device was removed in the second we were waiting? */ + if (IS_STARTED(ar)) { + dev_err(&aru->udev->dev, "no command feedback " + "received (%d).\n", err); + + /* provide some maybe useful debug information */ + print_hex_dump_bytes("ar9170 cmd: ", DUMP_PREFIX_NONE, + aru->common.cmdbuf, plen + 4); + dump_stack(); + } + + /* invalidate to avoid completing the next prematurely */ + spin_lock_irqsave(&aru->common.cmdlock, flags); + aru->readbuf = NULL; + aru->readlen = 0; + spin_unlock_irqrestore(&aru->common.cmdlock, flags); + +err_free: + + return err; +} + +static int ar9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ar9170_usb *aru = (struct ar9170_usb *) ar; + struct urb *urb; + + if (unlikely(!IS_STARTED(ar))) { + /* Seriously, what were you drink... err... thinking!? */ + return -EPERM; + } + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (unlikely(!urb)) + return -ENOMEM; + + usb_fill_bulk_urb(urb, aru->udev, + usb_sndbulkpipe(aru->udev, AR9170_EP_TX), + skb->data, skb->len, + ar9170_usb_tx_urb_complete_frame, skb); + urb->transfer_flags |= URB_ZERO_PACKET; + + usb_anchor_urb(urb, &aru->tx_pending); + aru->tx_pending_urbs++; + + usb_free_urb(urb); + + ar9170_usb_submit_urb(aru); + return 0; +} + +static void ar9170_usb_callback_cmd(struct ar9170 *ar, u32 len , void *buffer) +{ + struct ar9170_usb *aru = (void *) ar; + unsigned long flags; + u32 in, out; + + if (unlikely(!buffer)) + return ; + + in = le32_to_cpup((__le32 *)buffer); + out = le32_to_cpu(ar->cmdbuf[0]); + + /* mask off length byte */ + out &= ~0xFF; + + if (aru->readlen >= 0) { + /* add expected length */ + out |= aru->readlen; + } else { + /* add obtained length */ + out |= in & 0xFF; + } + + /* + * Some commands (e.g: AR9170_CMD_FREQUENCY) have a variable response + * length and we cannot predict the correct length in advance. + * So we only check if we provided enough space for the data. + */ + if (unlikely(out < in)) { + dev_warn(&aru->udev->dev, "received invalid command response " + "got %d bytes, instead of %d bytes " + "and the resp length is %d bytes\n", + in, out, len); + print_hex_dump_bytes("ar9170 invalid resp: ", + DUMP_PREFIX_OFFSET, buffer, len); + /* + * Do not complete, then the command times out, + * and we get a stack trace from there. + */ + return ; + } + + spin_lock_irqsave(&aru->common.cmdlock, flags); + if (aru->readbuf && len > 0) { + memcpy(aru->readbuf, buffer + 4, len - 4); + aru->readbuf = NULL; + } + complete(&aru->cmd_wait); + spin_unlock_irqrestore(&aru->common.cmdlock, flags); +} + +static int ar9170_usb_upload(struct ar9170_usb *aru, const void *data, + size_t len, u32 addr, bool complete) +{ + int transfer, err; + u8 *buf = kmalloc(4096, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + while (len) { + transfer = min_t(int, len, 4096); + memcpy(buf, data, transfer); + + err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0), + 0x30 /* FW DL */, 0x40 | USB_DIR_OUT, + addr >> 8, 0, buf, transfer, 1000); + + if (err < 0) { + kfree(buf); + return err; + } + + len -= transfer; + data += transfer; + addr += transfer; + } + kfree(buf); + + if (complete) { + err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0), + 0x31 /* FW DL COMPLETE */, + 0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 5000); + } + + return 0; +} + +static int ar9170_usb_request_firmware(struct ar9170_usb *aru) +{ + int err = 0; + + err = request_firmware(&aru->firmware, "ar9170.fw", + &aru->udev->dev); + if (!err) { + aru->init_values = NULL; + return 0; + } + + if (aru->req_one_stage_fw) { + dev_err(&aru->udev->dev, "ar9170.fw firmware file " + "not found and is required for this device\n"); + return -EINVAL; + } + + dev_err(&aru->udev->dev, "ar9170.fw firmware file " + "not found, trying old firmware...\n"); + + err = request_firmware(&aru->init_values, "ar9170-1.fw", + &aru->udev->dev); + + err = request_firmware(&aru->firmware, "ar9170-2.fw", &aru->udev->dev); + if (err) { + release_firmware(aru->init_values); + dev_err(&aru->udev->dev, "file with init values not found.\n"); + return err; + } + + return err; +} + +static int ar9170_usb_reset(struct ar9170_usb *aru) +{ + int ret, lock = (aru->intf->condition != USB_INTERFACE_BINDING); + + if (lock) { + ret = usb_lock_device_for_reset(aru->udev, aru->intf); + if (ret < 0) { + dev_err(&aru->udev->dev, "unable to lock device " + "for reset (%d).\n", ret); + return ret; + } + } + + ret = usb_reset_device(aru->udev); + if (lock) + usb_unlock_device(aru->udev); + + /* let it rest - for a second - */ + msleep(1000); + + return ret; +} + +static int ar9170_usb_upload_firmware(struct ar9170_usb *aru) +{ + int err; + + if (!aru->init_values) + goto upload_fw_start; + + /* First, upload initial values to device RAM */ + err = ar9170_usb_upload(aru, aru->init_values->data, + aru->init_values->size, 0x102800, false); + if (err) { + dev_err(&aru->udev->dev, "firmware part 1 " + "upload failed (%d).\n", err); + return err; + } + +upload_fw_start: + + /* Then, upload the firmware itself and start it */ + return ar9170_usb_upload(aru, aru->firmware->data, aru->firmware->size, + 0x200000, true); +} + +static int ar9170_usb_init_transport(struct ar9170_usb *aru) +{ + struct ar9170 *ar = (void *) &aru->common; + int err; + + ar9170_regwrite_begin(ar); + + /* Set USB Rx stream mode MAX packet number to 2 */ + ar9170_regwrite(AR9170_USB_REG_MAX_AGG_UPLOAD, 0x4); + + /* Set USB Rx stream mode timeout to 10us */ + ar9170_regwrite(AR9170_USB_REG_UPLOAD_TIME_CTL, 0x80); + + ar9170_regwrite_finish(); + + err = ar9170_regwrite_result(); + if (err) + dev_err(&aru->udev->dev, "USB setup failed (%d).\n", err); + + return err; +} + +static void ar9170_usb_stop(struct ar9170 *ar) +{ + struct ar9170_usb *aru = (void *) ar; + int ret; + + if (IS_ACCEPTING_CMD(ar)) + aru->common.state = AR9170_STOPPED; + + ret = ar9170_usb_flush(ar); + if (ret) + dev_err(&aru->udev->dev, "kill pending tx urbs.\n"); + + usb_poison_anchored_urbs(&aru->tx_submitted); + + /* + * Note: + * So far we freed all tx urbs, but we won't dare to touch any rx urbs. + * Else we would end up with a unresponsive device... + */ +} + +static int ar9170_usb_open(struct ar9170 *ar) +{ + struct ar9170_usb *aru = (void *) ar; + int err; + + usb_unpoison_anchored_urbs(&aru->tx_submitted); + err = ar9170_usb_init_transport(aru); + if (err) { + usb_poison_anchored_urbs(&aru->tx_submitted); + return err; + } + + aru->common.state = AR9170_IDLE; + return 0; +} + +static int ar9170_usb_init_device(struct ar9170_usb *aru) +{ + int err; + + err = ar9170_usb_alloc_rx_irq_urb(aru); + if (err) + goto err_out; + + err = ar9170_usb_alloc_rx_bulk_urbs(aru); + if (err) + goto err_unrx; + + err = ar9170_usb_upload_firmware(aru); + if (err) { + err = ar9170_echo_test(&aru->common, 0x60d43110); + if (err) { + /* force user invention, by disabling the device */ + err = usb_driver_set_configuration(aru->udev, -1); + dev_err(&aru->udev->dev, "device is in a bad state. " + "please reconnect it!\n"); + goto err_unrx; + } + } + + return 0; + +err_unrx: + ar9170_usb_cancel_urbs(aru); + +err_out: + return err; +} + +static bool ar9170_requires_one_stage(const struct usb_device_id *id) +{ + if (!id->driver_info) + return false; + if (id->driver_info == AR9170_REQ_FW1_ONLY) + return true; + return false; +} + +static int ar9170_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct ar9170_usb *aru; + struct ar9170 *ar; + struct usb_device *udev; + int err; + + aru = ar9170_alloc(sizeof(*aru)); + if (IS_ERR(aru)) { + err = PTR_ERR(aru); + goto out; + } + + udev = interface_to_usbdev(intf); + usb_get_dev(udev); + aru->udev = udev; + aru->intf = intf; + ar = &aru->common; + + aru->req_one_stage_fw = ar9170_requires_one_stage(id); + + usb_set_intfdata(intf, aru); + SET_IEEE80211_DEV(ar->hw, &udev->dev); + + init_usb_anchor(&aru->rx_submitted); + init_usb_anchor(&aru->tx_pending); + init_usb_anchor(&aru->tx_submitted); + init_completion(&aru->cmd_wait); + spin_lock_init(&aru->tx_urb_lock); + + aru->tx_pending_urbs = 0; + aru->tx_submitted_urbs = 0; + + aru->common.stop = ar9170_usb_stop; + aru->common.flush = ar9170_usb_flush; + aru->common.open = ar9170_usb_open; + aru->common.tx = ar9170_usb_tx; + aru->common.exec_cmd = ar9170_usb_exec_cmd; + aru->common.callback_cmd = ar9170_usb_callback_cmd; + +#ifdef CONFIG_PM + udev->reset_resume = 1; +#endif /* CONFIG_PM */ + err = ar9170_usb_reset(aru); + if (err) + goto err_freehw; + + err = ar9170_usb_request_firmware(aru); + if (err) + goto err_freehw; + + err = ar9170_usb_init_device(aru); + if (err) + goto err_freefw; + + err = ar9170_usb_open(ar); + if (err) + goto err_unrx; + + err = ar9170_register(ar, &udev->dev); + + ar9170_usb_stop(ar); + if (err) + goto err_unrx; + + return 0; + +err_unrx: + ar9170_usb_cancel_urbs(aru); + +err_freefw: + release_firmware(aru->init_values); + release_firmware(aru->firmware); + +err_freehw: + usb_set_intfdata(intf, NULL); + usb_put_dev(udev); + ieee80211_free_hw(ar->hw); +out: + return err; +} + +static void ar9170_usb_disconnect(struct usb_interface *intf) +{ + struct ar9170_usb *aru = usb_get_intfdata(intf); + + if (!aru) + return; + + aru->common.state = AR9170_IDLE; + ar9170_unregister(&aru->common); + ar9170_usb_cancel_urbs(aru); + + release_firmware(aru->init_values); + release_firmware(aru->firmware); + + usb_put_dev(aru->udev); + usb_set_intfdata(intf, NULL); + ieee80211_free_hw(aru->common.hw); +} + +#ifdef CONFIG_PM +static int ar9170_suspend(struct usb_interface *intf, + pm_message_t message) +{ + struct ar9170_usb *aru = usb_get_intfdata(intf); + + if (!aru) + return -ENODEV; + + aru->common.state = AR9170_IDLE; + ar9170_usb_cancel_urbs(aru); + + return 0; +} + +static int ar9170_resume(struct usb_interface *intf) +{ + struct ar9170_usb *aru = usb_get_intfdata(intf); + int err; + + if (!aru) + return -ENODEV; + + usb_unpoison_anchored_urbs(&aru->rx_submitted); + usb_unpoison_anchored_urbs(&aru->tx_submitted); + + err = ar9170_usb_init_device(aru); + if (err) + goto err_unrx; + + err = ar9170_usb_open(&aru->common); + if (err) + goto err_unrx; + + return 0; + +err_unrx: + aru->common.state = AR9170_IDLE; + ar9170_usb_cancel_urbs(aru); + + return err; +} +#endif /* CONFIG_PM */ + +static struct usb_driver ar9170_driver = { + .name = "ar9170usb", + .probe = ar9170_usb_probe, + .disconnect = ar9170_usb_disconnect, + .id_table = ar9170_usb_ids, + .soft_unbind = 1, +#ifdef CONFIG_PM + .suspend = ar9170_suspend, + .resume = ar9170_resume, + .reset_resume = ar9170_resume, +#endif /* CONFIG_PM */ +}; + +static int __init ar9170_init(void) +{ + return usb_register(&ar9170_driver); +} + +static void __exit ar9170_exit(void) +{ + usb_deregister(&ar9170_driver); +} + +module_init(ar9170_init); +module_exit(ar9170_exit); diff --git a/drivers/net/wireless/ath/ar9170/usb.h b/drivers/net/wireless/ath/ar9170/usb.h new file mode 100644 index 00000000000..d098f4d5d2f --- /dev/null +++ b/drivers/net/wireless/ath/ar9170/usb.h @@ -0,0 +1,81 @@ +/* + * Atheros AR9170 USB driver + * + * Driver specific definitions + * + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009, Christian Lamparter <chunkeey@web.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-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 __USB_H +#define __USB_H + +#include <linux/usb.h> +#include <linux/completion.h> +#include <linux/spinlock.h> +#include <linux/leds.h> +#include <net/cfg80211.h> +#include <net/mac80211.h> +#include <linux/firmware.h> +#include "eeprom.h" +#include "hw.h" +#include "ar9170.h" + +#define AR9170_NUM_RX_URBS 16 +#define AR9170_NUM_TX_URBS 8 + +struct firmware; + +struct ar9170_usb { + struct ar9170 common; + struct usb_device *udev; + struct usb_interface *intf; + + struct usb_anchor rx_submitted; + struct usb_anchor tx_pending; + struct usb_anchor tx_submitted; + + bool req_one_stage_fw; + + spinlock_t tx_urb_lock; + unsigned int tx_submitted_urbs; + unsigned int tx_pending_urbs; + + struct completion cmd_wait; + int readlen; + u8 *readbuf; + + const struct firmware *init_values; + const struct firmware *firmware; +}; + +#endif /* __USB_H */ diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig new file mode 100644 index 00000000000..509b6f94f73 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/Kconfig @@ -0,0 +1,41 @@ +config ATH5K + tristate "Atheros 5xxx wireless cards support" + depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL + select ATH_COMMON + select MAC80211_LEDS + select LEDS_CLASS + select NEW_LEDS + ---help--- + This module adds support for wireless adapters based on + Atheros 5xxx chipset. + + Currently the following chip versions are supported: + + MAC: AR5211 AR5212 + PHY: RF5111/2111 RF5112/2112 RF5413/2413 + + This driver uses the kernel's mac80211 subsystem. + + If you choose to build a module, it'll be called ath5k. Say M if + unsure. + +config ATH5K_DEBUG + bool "Atheros 5xxx debugging" + depends on ATH5K + ---help--- + Atheros 5xxx debugging messages. + + Say Y, if and you will get debug options for ath5k. + To use this, you need to mount debugfs: + + mkdir /debug/ + mount -t debugfs debug /debug/ + + You will get access to files under: + /debug/ath5k/phy0/ + + To enable debug, pass the debug level to the debug module + parameter. For example: + + modprobe ath5k debug=0x00000400 + diff --git a/drivers/net/wireless/ath/ath5k/Makefile b/drivers/net/wireless/ath/ath5k/Makefile new file mode 100644 index 00000000000..090dc6d268a --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/Makefile @@ -0,0 +1,16 @@ +ath5k-y += caps.o +ath5k-y += initvals.o +ath5k-y += eeprom.o +ath5k-y += gpio.o +ath5k-y += desc.o +ath5k-y += dma.o +ath5k-y += qcu.o +ath5k-y += pcu.o +ath5k-y += phy.o +ath5k-y += reset.o +ath5k-y += attach.o +ath5k-y += base.o +ath5k-y += led.o +ath5k-y += rfkill.o +ath5k-$(CONFIG_ATH5K_DEBUG) += debug.o +obj-$(CONFIG_ATH5K) += ath5k.o diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h new file mode 100644 index 00000000000..6358233bac9 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -0,0 +1,1379 @@ +/* + * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2007 Nick Kossifidis <mickflemm@gmail.com> + * + * Permission to use, copy, modify, and 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 _ATH5K_H +#define _ATH5K_H + +/* TODO: Clean up channel debuging -doesn't work anyway- and start + * working on reg. control code using all available eeprom information + * -rev. engineering needed- */ +#define CHAN_DEBUG 0 + +#include <linux/io.h> +#include <linux/types.h> +#include <net/mac80211.h> + +#include "../regd.h" + +/* RX/TX descriptor hw structs + * TODO: Driver part should only see sw structs */ +#include "desc.h" + +/* EEPROM structs/offsets + * TODO: Make a more generic struct (eg. add more stuff to ath5k_capabilities) + * and clean up common bits, then introduce set/get functions in eeprom.c */ +#include "eeprom.h" + +/* PCI IDs */ +#define PCI_DEVICE_ID_ATHEROS_AR5210 0x0007 /* AR5210 */ +#define PCI_DEVICE_ID_ATHEROS_AR5311 0x0011 /* AR5311 */ +#define PCI_DEVICE_ID_ATHEROS_AR5211 0x0012 /* AR5211 */ +#define PCI_DEVICE_ID_ATHEROS_AR5212 0x0013 /* AR5212 */ +#define PCI_DEVICE_ID_3COM_3CRDAG675 0x0013 /* 3CRDAG675 (Atheros AR5212) */ +#define PCI_DEVICE_ID_3COM_2_3CRPAG175 0x0013 /* 3CRPAG175 (Atheros AR5212) */ +#define PCI_DEVICE_ID_ATHEROS_AR5210_AP 0x0207 /* AR5210 (Early) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_IBM 0x1014 /* AR5212 (IBM MiniPCI) */ +#define PCI_DEVICE_ID_ATHEROS_AR5210_DEFAULT 0x1107 /* AR5210 (no eeprom) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_DEFAULT 0x1113 /* AR5212 (no eeprom) */ +#define PCI_DEVICE_ID_ATHEROS_AR5211_DEFAULT 0x1112 /* AR5211 (no eeprom) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_FPGA 0xf013 /* AR5212 (emulation board) */ +#define PCI_DEVICE_ID_ATHEROS_AR5211_LEGACY 0xff12 /* AR5211 (emulation board) */ +#define PCI_DEVICE_ID_ATHEROS_AR5211_FPGA11B 0xf11b /* AR5211 (emulation board) */ +#define PCI_DEVICE_ID_ATHEROS_AR5312_REV2 0x0052 /* AR5312 WMAC (AP31) */ +#define PCI_DEVICE_ID_ATHEROS_AR5312_REV7 0x0057 /* AR5312 WMAC (AP30-040) */ +#define PCI_DEVICE_ID_ATHEROS_AR5312_REV8 0x0058 /* AR5312 WMAC (AP43-030) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0014 0x0014 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0015 0x0015 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0016 0x0016 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0017 0x0017 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0018 0x0018 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0019 0x0019 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR2413 0x001a /* AR2413 (Griffin-lite) */ +#define PCI_DEVICE_ID_ATHEROS_AR5413 0x001b /* AR5413 (Eagle) */ +#define PCI_DEVICE_ID_ATHEROS_AR5424 0x001c /* AR5424 (Condor PCI-E) */ +#define PCI_DEVICE_ID_ATHEROS_AR5416 0x0023 /* AR5416 */ +#define PCI_DEVICE_ID_ATHEROS_AR5418 0x0024 /* AR5418 */ + +/****************************\ + GENERIC DRIVER DEFINITIONS +\****************************/ + +#define ATH5K_PRINTF(fmt, ...) printk("%s: " fmt, __func__, ##__VA_ARGS__) + +#define ATH5K_PRINTK(_sc, _level, _fmt, ...) \ + printk(_level "ath5k %s: " _fmt, \ + ((_sc) && (_sc)->hw) ? wiphy_name((_sc)->hw->wiphy) : "", \ + ##__VA_ARGS__) + +#define ATH5K_PRINTK_LIMIT(_sc, _level, _fmt, ...) do { \ + if (net_ratelimit()) \ + ATH5K_PRINTK(_sc, _level, _fmt, ##__VA_ARGS__); \ + } while (0) + +#define ATH5K_INFO(_sc, _fmt, ...) \ + ATH5K_PRINTK(_sc, KERN_INFO, _fmt, ##__VA_ARGS__) + +#define ATH5K_WARN(_sc, _fmt, ...) \ + ATH5K_PRINTK_LIMIT(_sc, KERN_WARNING, _fmt, ##__VA_ARGS__) + +#define ATH5K_ERR(_sc, _fmt, ...) \ + ATH5K_PRINTK_LIMIT(_sc, KERN_ERR, _fmt, ##__VA_ARGS__) + +/* + * AR5K REGISTER ACCESS + */ + +/* Some macros to read/write fields */ + +/* First shift, then mask */ +#define AR5K_REG_SM(_val, _flags) \ + (((_val) << _flags##_S) & (_flags)) + +/* First mask, then shift */ +#define AR5K_REG_MS(_val, _flags) \ + (((_val) & (_flags)) >> _flags##_S) + +/* Some registers can hold multiple values of interest. For this + * reason when we want to write to these registers we must first + * retrieve the values which we do not want to clear (lets call this + * old_data) and then set the register with this and our new_value: + * ( old_data | new_value) */ +#define AR5K_REG_WRITE_BITS(ah, _reg, _flags, _val) \ + ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, _reg) & ~(_flags)) | \ + (((_val) << _flags##_S) & (_flags)), _reg) + +#define AR5K_REG_MASKED_BITS(ah, _reg, _flags, _mask) \ + ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, _reg) & \ + (_mask)) | (_flags), _reg) + +#define AR5K_REG_ENABLE_BITS(ah, _reg, _flags) \ + ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) | (_flags), _reg) + +#define AR5K_REG_DISABLE_BITS(ah, _reg, _flags) \ + ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) & ~(_flags), _reg) + +/* Access to PHY registers */ +#define AR5K_PHY_READ(ah, _reg) \ + ath5k_hw_reg_read(ah, (ah)->ah_phy + ((_reg) << 2)) + +#define AR5K_PHY_WRITE(ah, _reg, _val) \ + ath5k_hw_reg_write(ah, _val, (ah)->ah_phy + ((_reg) << 2)) + +/* Access QCU registers per queue */ +#define AR5K_REG_READ_Q(ah, _reg, _queue) \ + (ath5k_hw_reg_read(ah, _reg) & (1 << _queue)) \ + +#define AR5K_REG_WRITE_Q(ah, _reg, _queue) \ + ath5k_hw_reg_write(ah, (1 << _queue), _reg) + +#define AR5K_Q_ENABLE_BITS(_reg, _queue) do { \ + _reg |= 1 << _queue; \ +} while (0) + +#define AR5K_Q_DISABLE_BITS(_reg, _queue) do { \ + _reg &= ~(1 << _queue); \ +} while (0) + +/* Used while writing initvals */ +#define AR5K_REG_WAIT(_i) do { \ + if (_i % 64) \ + udelay(1); \ +} while (0) + +/* Register dumps are done per operation mode */ +#define AR5K_INI_RFGAIN_5GHZ 0 +#define AR5K_INI_RFGAIN_2GHZ 1 + +/* TODO: Clean this up */ +#define AR5K_INI_VAL_11A 0 +#define AR5K_INI_VAL_11A_TURBO 1 +#define AR5K_INI_VAL_11B 2 +#define AR5K_INI_VAL_11G 3 +#define AR5K_INI_VAL_11G_TURBO 4 +#define AR5K_INI_VAL_XR 0 +#define AR5K_INI_VAL_MAX 5 + +/* Used for BSSID etc manipulation */ +#define AR5K_LOW_ID(_a)( \ +(_a)[0] | (_a)[1] << 8 | (_a)[2] << 16 | (_a)[3] << 24 \ +) + +#define AR5K_HIGH_ID(_a) ((_a)[4] | (_a)[5] << 8) + +/* + * Some tuneable values (these should be changeable by the user) + * TODO: Make use of them and add more options OR use debug/configfs + */ +#define AR5K_TUNE_DMA_BEACON_RESP 2 +#define AR5K_TUNE_SW_BEACON_RESP 10 +#define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF 0 +#define AR5K_TUNE_RADAR_ALERT false +#define AR5K_TUNE_MIN_TX_FIFO_THRES 1 +#define AR5K_TUNE_MAX_TX_FIFO_THRES ((IEEE80211_MAX_LEN / 64) + 1) +#define AR5K_TUNE_REGISTER_TIMEOUT 20000 +/* Register for RSSI threshold has a mask of 0xff, so 255 seems to + * be the max value. */ +#define AR5K_TUNE_RSSI_THRES 129 +/* This must be set when setting the RSSI threshold otherwise it can + * prevent a reset. If AR5K_RSSI_THR is read after writing to it + * the BMISS_THRES will be seen as 0, seems harware doesn't keep + * track of it. Max value depends on harware. For AR5210 this is just 7. + * For AR5211+ this seems to be up to 255. */ +#define AR5K_TUNE_BMISS_THRES 7 +#define AR5K_TUNE_REGISTER_DWELL_TIME 20000 +#define AR5K_TUNE_BEACON_INTERVAL 100 +#define AR5K_TUNE_AIFS 2 +#define AR5K_TUNE_AIFS_11B 2 +#define AR5K_TUNE_AIFS_XR 0 +#define AR5K_TUNE_CWMIN 15 +#define AR5K_TUNE_CWMIN_11B 31 +#define AR5K_TUNE_CWMIN_XR 3 +#define AR5K_TUNE_CWMAX 1023 +#define AR5K_TUNE_CWMAX_11B 1023 +#define AR5K_TUNE_CWMAX_XR 7 +#define AR5K_TUNE_NOISE_FLOOR -72 +#define AR5K_TUNE_MAX_TXPOWER 63 +#define AR5K_TUNE_DEFAULT_TXPOWER 25 +#define AR5K_TUNE_TPC_TXPOWER false +#define AR5K_TUNE_HWTXTRIES 4 + +#define AR5K_INIT_CARR_SENSE_EN 1 + +/*Swap RX/TX Descriptor for big endian archs*/ +#if defined(__BIG_ENDIAN) +#define AR5K_INIT_CFG ( \ + AR5K_CFG_SWTD | AR5K_CFG_SWRD \ +) +#else +#define AR5K_INIT_CFG 0x00000000 +#endif + +/* Initial values */ +#define AR5K_INIT_CYCRSSI_THR1 2 +#define AR5K_INIT_TX_LATENCY 502 +#define AR5K_INIT_USEC 39 +#define AR5K_INIT_USEC_TURBO 79 +#define AR5K_INIT_USEC_32 31 +#define AR5K_INIT_SLOT_TIME 396 +#define AR5K_INIT_SLOT_TIME_TURBO 480 +#define AR5K_INIT_ACK_CTS_TIMEOUT 1024 +#define AR5K_INIT_ACK_CTS_TIMEOUT_TURBO 0x08000800 +#define AR5K_INIT_PROG_IFS 920 +#define AR5K_INIT_PROG_IFS_TURBO 960 +#define AR5K_INIT_EIFS 3440 +#define AR5K_INIT_EIFS_TURBO 6880 +#define AR5K_INIT_SIFS 560 +#define AR5K_INIT_SIFS_TURBO 480 +#define AR5K_INIT_SH_RETRY 10 +#define AR5K_INIT_LG_RETRY AR5K_INIT_SH_RETRY +#define AR5K_INIT_SSH_RETRY 32 +#define AR5K_INIT_SLG_RETRY AR5K_INIT_SSH_RETRY +#define AR5K_INIT_TX_RETRY 10 + +#define AR5K_INIT_TRANSMIT_LATENCY ( \ + (AR5K_INIT_TX_LATENCY << 14) | (AR5K_INIT_USEC_32 << 7) | \ + (AR5K_INIT_USEC) \ +) +#define AR5K_INIT_TRANSMIT_LATENCY_TURBO ( \ + (AR5K_INIT_TX_LATENCY << 14) | (AR5K_INIT_USEC_32 << 7) | \ + (AR5K_INIT_USEC_TURBO) \ +) +#define AR5K_INIT_PROTO_TIME_CNTRL ( \ + (AR5K_INIT_CARR_SENSE_EN << 26) | (AR5K_INIT_EIFS << 12) | \ + (AR5K_INIT_PROG_IFS) \ +) +#define AR5K_INIT_PROTO_TIME_CNTRL_TURBO ( \ + (AR5K_INIT_CARR_SENSE_EN << 26) | (AR5K_INIT_EIFS_TURBO << 12) | \ + (AR5K_INIT_PROG_IFS_TURBO) \ +) + +/* token to use for aifs, cwmin, cwmax in MadWiFi */ +#define AR5K_TXQ_USEDEFAULT ((u32) -1) + +/* GENERIC CHIPSET DEFINITIONS */ + +/* MAC Chips */ +enum ath5k_version { + AR5K_AR5210 = 0, + AR5K_AR5211 = 1, + AR5K_AR5212 = 2, +}; + +/* PHY Chips */ +enum ath5k_radio { + AR5K_RF5110 = 0, + AR5K_RF5111 = 1, + AR5K_RF5112 = 2, + AR5K_RF2413 = 3, + AR5K_RF5413 = 4, + AR5K_RF2316 = 5, + AR5K_RF2317 = 6, + AR5K_RF2425 = 7, +}; + +/* + * Common silicon revision/version values + */ + +enum ath5k_srev_type { + AR5K_VERSION_MAC, + AR5K_VERSION_RAD, +}; + +struct ath5k_srev_name { + const char *sr_name; + enum ath5k_srev_type sr_type; + u_int sr_val; +}; + +#define AR5K_SREV_UNKNOWN 0xffff + +#define AR5K_SREV_AR5210 0x00 /* Crete */ +#define AR5K_SREV_AR5311 0x10 /* Maui 1 */ +#define AR5K_SREV_AR5311A 0x20 /* Maui 2 */ +#define AR5K_SREV_AR5311B 0x30 /* Spirit */ +#define AR5K_SREV_AR5211 0x40 /* Oahu */ +#define AR5K_SREV_AR5212 0x50 /* Venice */ +#define AR5K_SREV_AR5213 0x55 /* ??? */ +#define AR5K_SREV_AR5213A 0x59 /* Hainan */ +#define AR5K_SREV_AR2413 0x78 /* Griffin lite */ +#define AR5K_SREV_AR2414 0x70 /* Griffin */ +#define AR5K_SREV_AR5424 0x90 /* Condor */ +#define AR5K_SREV_AR5413 0xa4 /* Eagle lite */ +#define AR5K_SREV_AR5414 0xa0 /* Eagle */ +#define AR5K_SREV_AR2415 0xb0 /* Talon */ +#define AR5K_SREV_AR5416 0xc0 /* PCI-E */ +#define AR5K_SREV_AR5418 0xca /* PCI-E */ +#define AR5K_SREV_AR2425 0xe0 /* Swan */ +#define AR5K_SREV_AR2417 0xf0 /* Nala */ + +#define AR5K_SREV_RAD_5110 0x00 +#define AR5K_SREV_RAD_5111 0x10 +#define AR5K_SREV_RAD_5111A 0x15 +#define AR5K_SREV_RAD_2111 0x20 +#define AR5K_SREV_RAD_5112 0x30 +#define AR5K_SREV_RAD_5112A 0x35 +#define AR5K_SREV_RAD_5112B 0x36 +#define AR5K_SREV_RAD_2112 0x40 +#define AR5K_SREV_RAD_2112A 0x45 +#define AR5K_SREV_RAD_2112B 0x46 +#define AR5K_SREV_RAD_2413 0x50 +#define AR5K_SREV_RAD_5413 0x60 +#define AR5K_SREV_RAD_2316 0x70 /* Cobra SoC */ +#define AR5K_SREV_RAD_2317 0x80 +#define AR5K_SREV_RAD_5424 0xa0 /* Mostly same as 5413 */ +#define AR5K_SREV_RAD_2425 0xa2 +#define AR5K_SREV_RAD_5133 0xc0 + +#define AR5K_SREV_PHY_5211 0x30 +#define AR5K_SREV_PHY_5212 0x41 +#define AR5K_SREV_PHY_5212A 0x42 +#define AR5K_SREV_PHY_5212B 0x43 +#define AR5K_SREV_PHY_2413 0x45 +#define AR5K_SREV_PHY_5413 0x61 +#define AR5K_SREV_PHY_2425 0x70 + +/* IEEE defs */ +#define IEEE80211_MAX_LEN 2500 + +/* TODO add support to mac80211 for vendor-specific rates and modes */ + +/* + * Some of this information is based on Documentation from: + * + * http://madwifi.org/wiki/ChipsetFeatures/SuperAG + * + * Modulation for Atheros' eXtended Range - range enhancing extension that is + * supposed to double the distance an Atheros client device can keep a + * connection with an Atheros access point. This is achieved by increasing + * the receiver sensitivity up to, -105dBm, which is about 20dB above what + * the 802.11 specifications demand. In addition, new (proprietary) data rates + * are introduced: 3, 2, 1, 0.5 and 0.25 MBit/s. + * + * Please note that can you either use XR or TURBO but you cannot use both, + * they are exclusive. + * + */ +#define MODULATION_XR 0x00000200 +/* + * Modulation for Atheros' Turbo G and Turbo A, its supposed to provide a + * throughput transmission speed up to 40Mbit/s-60Mbit/s at a 108Mbit/s + * signaling rate achieved through the bonding of two 54Mbit/s 802.11g + * channels. To use this feature your Access Point must also suport it. + * There is also a distinction between "static" and "dynamic" turbo modes: + * + * - Static: is the dumb version: devices set to this mode stick to it until + * the mode is turned off. + * - Dynamic: is the intelligent version, the network decides itself if it + * is ok to use turbo. As soon as traffic is detected on adjacent channels + * (which would get used in turbo mode), or when a non-turbo station joins + * the network, turbo mode won't be used until the situation changes again. + * Dynamic mode is achieved by Atheros' Adaptive Radio (AR) feature which + * monitors the used radio band in order to decide whether turbo mode may + * be used or not. + * + * This article claims Super G sticks to bonding of channels 5 and 6 for + * USA: + * + * http://www.pcworld.com/article/id,113428-page,1/article.html + * + * The channel bonding seems to be driver specific though. In addition to + * deciding what channels will be used, these "Turbo" modes are accomplished + * by also enabling the following features: + * + * - Bursting: allows multiple frames to be sent at once, rather than pausing + * after each frame. Bursting is a standards-compliant feature that can be + * used with any Access Point. + * - Fast frames: increases the amount of information that can be sent per + * frame, also resulting in a reduction of transmission overhead. It is a + * proprietary feature that needs to be supported by the Access Point. + * - Compression: data frames are compressed in real time using a Lempel Ziv + * algorithm. This is done transparently. Once this feature is enabled, + * compression and decompression takes place inside the chipset, without + * putting additional load on the host CPU. + * + */ +#define MODULATION_TURBO 0x00000080 + +enum ath5k_driver_mode { + AR5K_MODE_11A = 0, + AR5K_MODE_11A_TURBO = 1, + AR5K_MODE_11B = 2, + AR5K_MODE_11G = 3, + AR5K_MODE_11G_TURBO = 4, + AR5K_MODE_XR = 0, + AR5K_MODE_MAX = 5 +}; + +enum ath5k_ant_mode { + AR5K_ANTMODE_DEFAULT = 0, /* default antenna setup */ + AR5K_ANTMODE_FIXED_A = 1, /* only antenna A is present */ + AR5K_ANTMODE_FIXED_B = 2, /* only antenna B is present */ + AR5K_ANTMODE_SINGLE_AP = 3, /* sta locked on a single ap */ + AR5K_ANTMODE_SECTOR_AP = 4, /* AP with tx antenna set on tx desc */ + AR5K_ANTMODE_SECTOR_STA = 5, /* STA with tx antenna set on tx desc */ + AR5K_ANTMODE_DEBUG = 6, /* Debug mode -A -> Rx, B-> Tx- */ + AR5K_ANTMODE_MAX, +}; + + +/****************\ + TX DEFINITIONS +\****************/ + +/* + * TX Status descriptor + */ +struct ath5k_tx_status { + u16 ts_seqnum; + u16 ts_tstamp; + u8 ts_status; + u8 ts_rate[4]; + u8 ts_retry[4]; + u8 ts_final_idx; + s8 ts_rssi; + u8 ts_shortretry; + u8 ts_longretry; + u8 ts_virtcol; + u8 ts_antenna; +}; + +#define AR5K_TXSTAT_ALTRATE 0x80 +#define AR5K_TXERR_XRETRY 0x01 +#define AR5K_TXERR_FILT 0x02 +#define AR5K_TXERR_FIFO 0x04 + +/** + * enum ath5k_tx_queue - Queue types used to classify tx queues. + * @AR5K_TX_QUEUE_INACTIVE: q is unused -- see ath5k_hw_release_tx_queue + * @AR5K_TX_QUEUE_DATA: A normal data queue + * @AR5K_TX_QUEUE_XR_DATA: An XR-data queue + * @AR5K_TX_QUEUE_BEACON: The beacon queue + * @AR5K_TX_QUEUE_CAB: The after-beacon queue + * @AR5K_TX_QUEUE_UAPSD: Unscheduled Automatic Power Save Delivery queue + */ +enum ath5k_tx_queue { + AR5K_TX_QUEUE_INACTIVE = 0, + AR5K_TX_QUEUE_DATA, + AR5K_TX_QUEUE_XR_DATA, + AR5K_TX_QUEUE_BEACON, + AR5K_TX_QUEUE_CAB, + AR5K_TX_QUEUE_UAPSD, +}; + +#define AR5K_NUM_TX_QUEUES 10 +#define AR5K_NUM_TX_QUEUES_NOQCU 2 + +/* + * Queue syb-types to classify normal data queues. + * These are the 4 Access Categories as defined in + * WME spec. 0 is the lowest priority and 4 is the + * highest. Normal data that hasn't been classified + * goes to the Best Effort AC. + */ +enum ath5k_tx_queue_subtype { + AR5K_WME_AC_BK = 0, /*Background traffic*/ + AR5K_WME_AC_BE, /*Best-effort (normal) traffic)*/ + AR5K_WME_AC_VI, /*Video traffic*/ + AR5K_WME_AC_VO, /*Voice traffic*/ +}; + +/* + * Queue ID numbers as returned by the hw functions, each number + * represents a hw queue. If hw does not support hw queues + * (eg 5210) all data goes in one queue. These match + * d80211 definitions (net80211/MadWiFi don't use them). + */ +enum ath5k_tx_queue_id { + AR5K_TX_QUEUE_ID_NOQCU_DATA = 0, + AR5K_TX_QUEUE_ID_NOQCU_BEACON = 1, + AR5K_TX_QUEUE_ID_DATA_MIN = 0, /*IEEE80211_TX_QUEUE_DATA0*/ + AR5K_TX_QUEUE_ID_DATA_MAX = 4, /*IEEE80211_TX_QUEUE_DATA4*/ + AR5K_TX_QUEUE_ID_DATA_SVP = 5, /*IEEE80211_TX_QUEUE_SVP - Spectralink Voice Protocol*/ + AR5K_TX_QUEUE_ID_CAB = 6, /*IEEE80211_TX_QUEUE_AFTER_BEACON*/ + AR5K_TX_QUEUE_ID_BEACON = 7, /*IEEE80211_TX_QUEUE_BEACON*/ + AR5K_TX_QUEUE_ID_UAPSD = 8, + AR5K_TX_QUEUE_ID_XR_DATA = 9, +}; + +/* + * Flags to set hw queue's parameters... + */ +#define AR5K_TXQ_FLAG_TXOKINT_ENABLE 0x0001 /* Enable TXOK interrupt */ +#define AR5K_TXQ_FLAG_TXERRINT_ENABLE 0x0002 /* Enable TXERR interrupt */ +#define AR5K_TXQ_FLAG_TXEOLINT_ENABLE 0x0004 /* Enable TXEOL interrupt -not used- */ +#define AR5K_TXQ_FLAG_TXDESCINT_ENABLE 0x0008 /* Enable TXDESC interrupt -not used- */ +#define AR5K_TXQ_FLAG_TXURNINT_ENABLE 0x0010 /* Enable TXURN interrupt */ +#define AR5K_TXQ_FLAG_CBRORNINT_ENABLE 0x0020 /* Enable CBRORN interrupt */ +#define AR5K_TXQ_FLAG_CBRURNINT_ENABLE 0x0040 /* Enable CBRURN interrupt */ +#define AR5K_TXQ_FLAG_QTRIGINT_ENABLE 0x0080 /* Enable QTRIG interrupt */ +#define AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE 0x0100 /* Enable TXNOFRM interrupt */ +#define AR5K_TXQ_FLAG_BACKOFF_DISABLE 0x0200 /* Disable random post-backoff */ +#define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE 0x0300 /* Enable ready time expiry policy (?)*/ +#define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE 0x0800 /* Enable backoff while bursting */ +#define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS 0x1000 /* Disable backoff while bursting */ +#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE 0x2000 /* Enable hw compression -not implemented-*/ + +/* + * A struct to hold tx queue's parameters + */ +struct ath5k_txq_info { + enum ath5k_tx_queue tqi_type; + enum ath5k_tx_queue_subtype tqi_subtype; + u16 tqi_flags; /* Tx queue flags (see above) */ + u32 tqi_aifs; /* Arbitrated Interframe Space */ + s32 tqi_cw_min; /* Minimum Contention Window */ + s32 tqi_cw_max; /* Maximum Contention Window */ + u32 tqi_cbr_period; /* Constant bit rate period */ + u32 tqi_cbr_overflow_limit; + u32 tqi_burst_time; + u32 tqi_ready_time; /* Not used */ +}; + +/* + * Transmit packet types. + * used on tx control descriptor + * TODO: Use them inside base.c corectly + */ +enum ath5k_pkt_type { + AR5K_PKT_TYPE_NORMAL = 0, + AR5K_PKT_TYPE_ATIM = 1, + AR5K_PKT_TYPE_PSPOLL = 2, + AR5K_PKT_TYPE_BEACON = 3, + AR5K_PKT_TYPE_PROBE_RESP = 4, + AR5K_PKT_TYPE_PIFS = 5, +}; + +/* + * TX power and TPC settings + */ +#define AR5K_TXPOWER_OFDM(_r, _v) ( \ + ((0 & 1) << ((_v) + 6)) | \ + (((ah->ah_txpower.txp_rates_power_table[(_r)]) & 0x3f) << (_v)) \ +) + +#define AR5K_TXPOWER_CCK(_r, _v) ( \ + (ah->ah_txpower.txp_rates_power_table[(_r)] & 0x3f) << (_v) \ +) + +/* + * DMA size definitions (2^n+2) + */ +enum ath5k_dmasize { + AR5K_DMASIZE_4B = 0, + AR5K_DMASIZE_8B, + AR5K_DMASIZE_16B, + AR5K_DMASIZE_32B, + AR5K_DMASIZE_64B, + AR5K_DMASIZE_128B, + AR5K_DMASIZE_256B, + AR5K_DMASIZE_512B +}; + + +/****************\ + RX DEFINITIONS +\****************/ + +/* + * RX Status descriptor + */ +struct ath5k_rx_status { + u16 rs_datalen; + u16 rs_tstamp; + u8 rs_status; + u8 rs_phyerr; + s8 rs_rssi; + u8 rs_keyix; + u8 rs_rate; + u8 rs_antenna; + u8 rs_more; +}; + +#define AR5K_RXERR_CRC 0x01 +#define AR5K_RXERR_PHY 0x02 +#define AR5K_RXERR_FIFO 0x04 +#define AR5K_RXERR_DECRYPT 0x08 +#define AR5K_RXERR_MIC 0x10 +#define AR5K_RXKEYIX_INVALID ((u8) - 1) +#define AR5K_TXKEYIX_INVALID ((u32) - 1) + + +/**************************\ + BEACON TIMERS DEFINITIONS +\**************************/ + +#define AR5K_BEACON_PERIOD 0x0000ffff +#define AR5K_BEACON_ENA 0x00800000 /*enable beacon xmit*/ +#define AR5K_BEACON_RESET_TSF 0x01000000 /*force a TSF reset*/ + +#if 0 +/** + * struct ath5k_beacon_state - Per-station beacon timer state. + * @bs_interval: in TU's, can also include the above flags + * @bs_cfp_max_duration: if non-zero hw is setup to coexist with a + * Point Coordination Function capable AP + */ +struct ath5k_beacon_state { + u32 bs_next_beacon; + u32 bs_next_dtim; + u32 bs_interval; + u8 bs_dtim_period; + u8 bs_cfp_period; + u16 bs_cfp_max_duration; + u16 bs_cfp_du_remain; + u16 bs_tim_offset; + u16 bs_sleep_duration; + u16 bs_bmiss_threshold; + u32 bs_cfp_next; +}; +#endif + + +/* + * TSF to TU conversion: + * + * TSF is a 64bit value in usec (microseconds). + * TU is a 32bit value and defined by IEEE802.11 (page 6) as "A measurement of + * time equal to 1024 usec", so it's roughly milliseconds (usec / 1024). + */ +#define TSF_TO_TU(_tsf) (u32)((_tsf) >> 10) + + +/*******************************\ + GAIN OPTIMIZATION DEFINITIONS +\*******************************/ + +enum ath5k_rfgain { + AR5K_RFGAIN_INACTIVE = 0, + AR5K_RFGAIN_ACTIVE, + AR5K_RFGAIN_READ_REQUESTED, + AR5K_RFGAIN_NEED_CHANGE, +}; + +struct ath5k_gain { + u8 g_step_idx; + u8 g_current; + u8 g_target; + u8 g_low; + u8 g_high; + u8 g_f_corr; + u8 g_state; +}; + +/********************\ + COMMON DEFINITIONS +\********************/ + +#define AR5K_SLOT_TIME_9 396 +#define AR5K_SLOT_TIME_20 880 +#define AR5K_SLOT_TIME_MAX 0xffff + +/* channel_flags */ +#define CHANNEL_CW_INT 0x0008 /* Contention Window interference detected */ +#define CHANNEL_TURBO 0x0010 /* Turbo Channel */ +#define CHANNEL_CCK 0x0020 /* CCK channel */ +#define CHANNEL_OFDM 0x0040 /* OFDM channel */ +#define CHANNEL_2GHZ 0x0080 /* 2GHz channel. */ +#define CHANNEL_5GHZ 0x0100 /* 5GHz channel */ +#define CHANNEL_PASSIVE 0x0200 /* Only passive scan allowed */ +#define CHANNEL_DYN 0x0400 /* Dynamic CCK-OFDM channel (for g operation) */ +#define CHANNEL_XR 0x0800 /* XR channel */ + +#define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM) +#define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK) +#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM) +#define CHANNEL_T (CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_TURBO) +#define CHANNEL_TG (CHANNEL_2GHZ|CHANNEL_OFDM|CHANNEL_TURBO) +#define CHANNEL_108A CHANNEL_T +#define CHANNEL_108G CHANNEL_TG +#define CHANNEL_X (CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_XR) + +#define CHANNEL_ALL (CHANNEL_OFDM|CHANNEL_CCK|CHANNEL_2GHZ|CHANNEL_5GHZ| \ + CHANNEL_TURBO) + +#define CHANNEL_ALL_NOTURBO (CHANNEL_ALL & ~CHANNEL_TURBO) +#define CHANNEL_MODES CHANNEL_ALL + +/* + * Used internaly for reset_tx_queue). + * Also see struct struct ieee80211_channel. + */ +#define IS_CHAN_XR(_c) ((_c.hw_value & CHANNEL_XR) != 0) +#define IS_CHAN_B(_c) ((_c.hw_value & CHANNEL_B) != 0) + +/* + * The following structure is used to map 2GHz channels to + * 5GHz Atheros channels. + * TODO: Clean up + */ +struct ath5k_athchan_2ghz { + u32 a2_flags; + u16 a2_athchan; +}; + + +/******************\ + RATE DEFINITIONS +\******************/ + +/** + * Seems the ar5xxx harware supports up to 32 rates, indexed by 1-32. + * + * The rate code is used to get the RX rate or set the TX rate on the + * hardware descriptors. It is also used for internal modulation control + * and settings. + * + * This is the hardware rate map we are aware of: + * + * rate_code 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 + * rate_kbps 3000 1000 ? ? ? 2000 500 48000 + * + * rate_code 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 + * rate_kbps 24000 12000 6000 54000 36000 18000 9000 ? + * + * rate_code 17 18 19 20 21 22 23 24 + * rate_kbps ? ? ? ? ? ? ? 11000 + * + * rate_code 25 26 27 28 29 30 31 32 + * rate_kbps 5500 2000 1000 11000S 5500S 2000S ? ? + * + * "S" indicates CCK rates with short preamble. + * + * AR5211 has different rate codes for CCK (802.11B) rates. It only uses the + * lowest 4 bits, so they are the same as below with a 0xF mask. + * (0xB, 0xA, 0x9 and 0x8 for 1M, 2M, 5.5M and 11M). + * We handle this in ath5k_setup_bands(). + */ +#define AR5K_MAX_RATES 32 + +/* B */ +#define ATH5K_RATE_CODE_1M 0x1B +#define ATH5K_RATE_CODE_2M 0x1A +#define ATH5K_RATE_CODE_5_5M 0x19 +#define ATH5K_RATE_CODE_11M 0x18 +/* A and G */ +#define ATH5K_RATE_CODE_6M 0x0B +#define ATH5K_RATE_CODE_9M 0x0F +#define ATH5K_RATE_CODE_12M 0x0A +#define ATH5K_RATE_CODE_18M 0x0E +#define ATH5K_RATE_CODE_24M 0x09 +#define ATH5K_RATE_CODE_36M 0x0D +#define ATH5K_RATE_CODE_48M 0x08 +#define ATH5K_RATE_CODE_54M 0x0C +/* XR */ +#define ATH5K_RATE_CODE_XR_500K 0x07 +#define ATH5K_RATE_CODE_XR_1M 0x02 +#define ATH5K_RATE_CODE_XR_2M 0x06 +#define ATH5K_RATE_CODE_XR_3M 0x01 + +/* adding this flag to rate_code enables short preamble */ +#define AR5K_SET_SHORT_PREAMBLE 0x04 + +/* + * Crypto definitions + */ + +#define AR5K_KEYCACHE_SIZE 8 + +/***********************\ + HW RELATED DEFINITIONS +\***********************/ + +/* + * Misc definitions + */ +#define AR5K_RSSI_EP_MULTIPLIER (1<<7) + +#define AR5K_ASSERT_ENTRY(_e, _s) do { \ + if (_e >= _s) \ + return (false); \ +} while (0) + +/* + * Hardware interrupt abstraction + */ + +/** + * enum ath5k_int - Hardware interrupt masks helpers + * + * @AR5K_INT_RX: mask to identify received frame interrupts, of type + * AR5K_ISR_RXOK or AR5K_ISR_RXERR + * @AR5K_INT_RXDESC: Request RX descriptor/Read RX descriptor (?) + * @AR5K_INT_RXNOFRM: No frame received (?) + * @AR5K_INT_RXEOL: received End Of List for VEOL (Virtual End Of List). The + * Queue Control Unit (QCU) signals an EOL interrupt only if a descriptor's + * LinkPtr is NULL. For more details, refer to: + * http://www.freepatentsonline.com/20030225739.html + * @AR5K_INT_RXORN: Indicates we got RX overrun (eg. no more descriptors). + * Note that Rx overrun is not always fatal, on some chips we can continue + * operation without reseting the card, that's why int_fatal is not + * common for all chips. + * @AR5K_INT_TX: mask to identify received frame interrupts, of type + * AR5K_ISR_TXOK or AR5K_ISR_TXERR + * @AR5K_INT_TXDESC: Request TX descriptor/Read TX status descriptor (?) + * @AR5K_INT_TXURN: received when we should increase the TX trigger threshold + * We currently do increments on interrupt by + * (AR5K_TUNE_MAX_TX_FIFO_THRES - current_trigger_level) / 2 + * @AR5K_INT_MIB: Indicates the Management Information Base counters should be + * checked. We should do this with ath5k_hw_update_mib_counters() but + * it seems we should also then do some noise immunity work. + * @AR5K_INT_RXPHY: RX PHY Error + * @AR5K_INT_RXKCM: RX Key cache miss + * @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a + * beacon that must be handled in software. The alternative is if you + * have VEOL support, in that case you let the hardware deal with things. + * @AR5K_INT_BMISS: If in STA mode this indicates we have stopped seeing + * beacons from the AP have associated with, we should probably try to + * reassociate. When in IBSS mode this might mean we have not received + * any beacons from any local stations. Note that every station in an + * IBSS schedules to send beacons at the Target Beacon Transmission Time + * (TBTT) with a random backoff. + * @AR5K_INT_BNR: Beacon Not Ready interrupt - ?? + * @AR5K_INT_GPIO: GPIO interrupt is used for RF Kill, disabled for now + * until properly handled + * @AR5K_INT_FATAL: Fatal errors were encountered, typically caused by DMA + * errors. These types of errors we can enable seem to be of type + * AR5K_SIMR2_MCABT, AR5K_SIMR2_SSERR and AR5K_SIMR2_DPERR. + * @AR5K_INT_GLOBAL: Used to clear and set the IER + * @AR5K_INT_NOCARD: signals the card has been removed + * @AR5K_INT_COMMON: common interrupts shared amogst MACs with the same + * bit value + * + * These are mapped to take advantage of some common bits + * between the MACs, to be able to set intr properties + * easier. Some of them are not used yet inside hw.c. Most map + * to the respective hw interrupt value as they are common amogst different + * MACs. + */ +enum ath5k_int { + AR5K_INT_RXOK = 0x00000001, + AR5K_INT_RXDESC = 0x00000002, + AR5K_INT_RXERR = 0x00000004, + AR5K_INT_RXNOFRM = 0x00000008, + AR5K_INT_RXEOL = 0x00000010, + AR5K_INT_RXORN = 0x00000020, + AR5K_INT_TXOK = 0x00000040, + AR5K_INT_TXDESC = 0x00000080, + AR5K_INT_TXERR = 0x00000100, + AR5K_INT_TXNOFRM = 0x00000200, + AR5K_INT_TXEOL = 0x00000400, + AR5K_INT_TXURN = 0x00000800, + AR5K_INT_MIB = 0x00001000, + AR5K_INT_SWI = 0x00002000, + AR5K_INT_RXPHY = 0x00004000, + AR5K_INT_RXKCM = 0x00008000, + AR5K_INT_SWBA = 0x00010000, + AR5K_INT_BRSSI = 0x00020000, + AR5K_INT_BMISS = 0x00040000, + AR5K_INT_FATAL = 0x00080000, /* Non common */ + AR5K_INT_BNR = 0x00100000, /* Non common */ + AR5K_INT_TIM = 0x00200000, /* Non common */ + AR5K_INT_DTIM = 0x00400000, /* Non common */ + AR5K_INT_DTIM_SYNC = 0x00800000, /* Non common */ + AR5K_INT_GPIO = 0x01000000, + AR5K_INT_BCN_TIMEOUT = 0x02000000, /* Non common */ + AR5K_INT_CAB_TIMEOUT = 0x04000000, /* Non common */ + AR5K_INT_RX_DOPPLER = 0x08000000, /* Non common */ + AR5K_INT_QCBRORN = 0x10000000, /* Non common */ + AR5K_INT_QCBRURN = 0x20000000, /* Non common */ + AR5K_INT_QTRIG = 0x40000000, /* Non common */ + AR5K_INT_GLOBAL = 0x80000000, + + AR5K_INT_COMMON = AR5K_INT_RXOK + | AR5K_INT_RXDESC + | AR5K_INT_RXERR + | AR5K_INT_RXNOFRM + | AR5K_INT_RXEOL + | AR5K_INT_RXORN + | AR5K_INT_TXOK + | AR5K_INT_TXDESC + | AR5K_INT_TXERR + | AR5K_INT_TXNOFRM + | AR5K_INT_TXEOL + | AR5K_INT_TXURN + | AR5K_INT_MIB + | AR5K_INT_SWI + | AR5K_INT_RXPHY + | AR5K_INT_RXKCM + | AR5K_INT_SWBA + | AR5K_INT_BRSSI + | AR5K_INT_BMISS + | AR5K_INT_GPIO + | AR5K_INT_GLOBAL, + + AR5K_INT_NOCARD = 0xffffffff +}; + +/* + * Power management + */ +enum ath5k_power_mode { + AR5K_PM_UNDEFINED = 0, + AR5K_PM_AUTO, + AR5K_PM_AWAKE, + AR5K_PM_FULL_SLEEP, + AR5K_PM_NETWORK_SLEEP, +}; + +/* + * These match net80211 definitions (not used in + * mac80211). + * TODO: Clean this up + */ +#define AR5K_LED_INIT 0 /*IEEE80211_S_INIT*/ +#define AR5K_LED_SCAN 1 /*IEEE80211_S_SCAN*/ +#define AR5K_LED_AUTH 2 /*IEEE80211_S_AUTH*/ +#define AR5K_LED_ASSOC 3 /*IEEE80211_S_ASSOC*/ +#define AR5K_LED_RUN 4 /*IEEE80211_S_RUN*/ + +/* GPIO-controlled software LED */ +#define AR5K_SOFTLED_PIN 0 +#define AR5K_SOFTLED_ON 0 +#define AR5K_SOFTLED_OFF 1 + +/* + * Chipset capabilities -see ath5k_hw_get_capability- + * get_capability function is not yet fully implemented + * in ath5k so most of these don't work yet... + * TODO: Implement these & merge with _TUNE_ stuff above + */ +enum ath5k_capability_type { + AR5K_CAP_REG_DMN = 0, /* Used to get current reg. domain id */ + AR5K_CAP_TKIP_MIC = 2, /* Can handle TKIP MIC in hardware */ + AR5K_CAP_TKIP_SPLIT = 3, /* TKIP uses split keys */ + AR5K_CAP_PHYCOUNTERS = 4, /* PHY error counters */ + AR5K_CAP_DIVERSITY = 5, /* Supports fast diversity */ + AR5K_CAP_NUM_TXQUEUES = 6, /* Used to get max number of hw txqueues */ + AR5K_CAP_VEOL = 7, /* Supports virtual EOL */ + AR5K_CAP_COMPRESSION = 8, /* Supports compression */ + AR5K_CAP_BURST = 9, /* Supports packet bursting */ + AR5K_CAP_FASTFRAME = 10, /* Supports fast frames */ + AR5K_CAP_TXPOW = 11, /* Used to get global tx power limit */ + AR5K_CAP_TPC = 12, /* Can do per-packet tx power control (needed for 802.11a) */ + AR5K_CAP_BSSIDMASK = 13, /* Supports bssid mask */ + AR5K_CAP_MCAST_KEYSRCH = 14, /* Supports multicast key search */ + AR5K_CAP_TSF_ADJUST = 15, /* Supports beacon tsf adjust */ + AR5K_CAP_XR = 16, /* Supports XR mode */ + AR5K_CAP_WME_TKIPMIC = 17, /* Supports TKIP MIC when using WMM */ + AR5K_CAP_CHAN_HALFRATE = 18, /* Supports half rate channels */ + AR5K_CAP_CHAN_QUARTERRATE = 19, /* Supports quarter rate channels */ + AR5K_CAP_RFSILENT = 20, /* Supports RFsilent */ +}; + + +/* XXX: we *may* move cap_range stuff to struct wiphy */ +struct ath5k_capabilities { + /* + * Supported PHY modes + * (ie. CHANNEL_A, CHANNEL_B, ...) + */ + DECLARE_BITMAP(cap_mode, AR5K_MODE_MAX); + + /* + * Frequency range (without regulation restrictions) + */ + struct { + u16 range_2ghz_min; + u16 range_2ghz_max; + u16 range_5ghz_min; + u16 range_5ghz_max; + } cap_range; + + /* + * Values stored in the EEPROM (some of them...) + */ + struct ath5k_eeprom_info cap_eeprom; + + /* + * Queue information + */ + struct { + u8 q_tx_num; + } cap_queues; +}; + + +/***************************************\ + HARDWARE ABSTRACTION LAYER STRUCTURE +\***************************************/ + +/* + * Misc defines + */ + +#define AR5K_MAX_GPIO 10 +#define AR5K_MAX_RF_BANKS 8 + +/* TODO: Clean up and merge with ath5k_softc */ +struct ath5k_hw { + u32 ah_magic; + + struct ath5k_softc *ah_sc; + void __iomem *ah_iobase; + + enum ath5k_int ah_imr; + + enum nl80211_iftype ah_op_mode; + enum ath5k_power_mode ah_power_mode; + struct ieee80211_channel ah_current_channel; + bool ah_turbo; + bool ah_calibration; + bool ah_running; + bool ah_single_chip; + bool ah_combined_mic; + + u32 ah_mac_srev; + u16 ah_mac_version; + u16 ah_mac_revision; + u16 ah_phy_revision; + u16 ah_radio_5ghz_revision; + u16 ah_radio_2ghz_revision; + + enum ath5k_version ah_version; + enum ath5k_radio ah_radio; + u32 ah_phy; + + bool ah_5ghz; + bool ah_2ghz; + +#define ah_modes ah_capabilities.cap_mode +#define ah_ee_version ah_capabilities.cap_eeprom.ee_version + + u32 ah_atim_window; + u32 ah_aifs; + u32 ah_cw_min; + u32 ah_cw_max; + bool ah_software_retry; + u32 ah_limit_tx_retries; + + /* Antenna Control */ + u32 ah_ant_ctl[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; + u8 ah_ant_mode; + u8 ah_tx_ant; + u8 ah_def_ant; + + u8 ah_sta_id[ETH_ALEN]; + + /* Current BSSID we are trying to assoc to / create. + * This is passed by mac80211 on config_interface() and cached here for + * use in resets */ + u8 ah_bssid[ETH_ALEN]; + u8 ah_bssid_mask[ETH_ALEN]; + + u32 ah_gpio[AR5K_MAX_GPIO]; + int ah_gpio_npins; + + struct ath_regulatory ah_regulatory; + struct ath5k_capabilities ah_capabilities; + + struct ath5k_txq_info ah_txq[AR5K_NUM_TX_QUEUES]; + u32 ah_txq_status; + u32 ah_txq_imr_txok; + u32 ah_txq_imr_txerr; + u32 ah_txq_imr_txurn; + u32 ah_txq_imr_txdesc; + u32 ah_txq_imr_txeol; + u32 ah_txq_imr_cbrorn; + u32 ah_txq_imr_cbrurn; + u32 ah_txq_imr_qtrig; + u32 ah_txq_imr_nofrm; + u32 ah_txq_isr; + u32 *ah_rf_banks; + size_t ah_rf_banks_size; + size_t ah_rf_regs_count; + struct ath5k_gain ah_gain; + u8 ah_offset[AR5K_MAX_RF_BANKS]; + + + struct { + /* Temporary tables used for interpolation */ + u8 tmpL[AR5K_EEPROM_N_PD_GAINS] + [AR5K_EEPROM_POWER_TABLE_SIZE]; + u8 tmpR[AR5K_EEPROM_N_PD_GAINS] + [AR5K_EEPROM_POWER_TABLE_SIZE]; + u8 txp_pd_table[AR5K_EEPROM_POWER_TABLE_SIZE * 2]; + u16 txp_rates_power_table[AR5K_MAX_RATES]; + u8 txp_min_idx; + bool txp_tpc; + /* Values in 0.25dB units */ + s16 txp_min_pwr; + s16 txp_max_pwr; + /* Values in 0.5dB units */ + s16 txp_offset; + s16 txp_ofdm; + s16 txp_cck_ofdm_gainf_delta; + /* Value in dB units */ + s16 txp_cck_ofdm_pwr_delta; + } ah_txpower; + + struct { + bool r_enabled; + int r_last_alert; + struct ieee80211_channel r_last_channel; + } ah_radar; + + /* noise floor from last periodic calibration */ + s32 ah_noise_floor; + + /* + * Function pointers + */ + int (*ah_setup_rx_desc)(struct ath5k_hw *ah, struct ath5k_desc *desc, + u32 size, unsigned int flags); + int (*ah_setup_tx_desc)(struct ath5k_hw *, struct ath5k_desc *, + unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int, + unsigned int, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int); + int (*ah_setup_mrr_tx_desc)(struct ath5k_hw *, struct ath5k_desc *, + unsigned int, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int); + int (*ah_proc_tx_desc)(struct ath5k_hw *, struct ath5k_desc *, + struct ath5k_tx_status *); + int (*ah_proc_rx_desc)(struct ath5k_hw *, struct ath5k_desc *, + struct ath5k_rx_status *); +}; + +/* + * Prototypes + */ + +/* Attach/Detach Functions */ +extern struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version); +extern void ath5k_hw_detach(struct ath5k_hw *ah); + +/* LED functions */ +extern int ath5k_init_leds(struct ath5k_softc *sc); +extern void ath5k_led_enable(struct ath5k_softc *sc); +extern void ath5k_led_off(struct ath5k_softc *sc); +extern void ath5k_unregister_leds(struct ath5k_softc *sc); + +/* Reset Functions */ +extern int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial); +extern int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool change_channel); +/* Power management functions */ +extern int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration); + +/* DMA Related Functions */ +extern void ath5k_hw_start_rx_dma(struct ath5k_hw *ah); +extern int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah); +extern u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah); +extern void ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr); +extern int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue); +extern int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue); +extern u32 ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue); +extern int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, + u32 phys_addr); +extern int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase); +/* Interrupt handling */ +extern bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah); +extern int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask); +extern enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum +ath5k_int new_mask); +extern void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, struct ieee80211_low_level_stats *stats); + +/* EEPROM access functions */ +extern int ath5k_eeprom_init(struct ath5k_hw *ah); +extern void ath5k_eeprom_detach(struct ath5k_hw *ah); +extern int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac); +extern bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah); + +/* Protocol Control Unit Functions */ +extern int ath5k_hw_set_opmode(struct ath5k_hw *ah); +/* BSSID Functions */ +extern void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac); +extern int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac); +extern void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id); +extern int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask); +/* Receive start/stop functions */ +extern void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah); +extern void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah); +/* RX Filter functions */ +extern void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1); +extern int ath5k_hw_set_mcast_filter_idx(struct ath5k_hw *ah, u32 index); +extern int ath5k_hw_clear_mcast_filter_idx(struct ath5k_hw *ah, u32 index); +extern u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah); +extern void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter); +/* Beacon control functions */ +extern u32 ath5k_hw_get_tsf32(struct ath5k_hw *ah); +extern u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah); +extern void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64); +extern void ath5k_hw_reset_tsf(struct ath5k_hw *ah); +extern void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval); +#if 0 +extern int ath5k_hw_set_beacon_timers(struct ath5k_hw *ah, const struct ath5k_beacon_state *state); +extern void ath5k_hw_reset_beacon(struct ath5k_hw *ah); +extern int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr); +#endif +/* ACK bit rate */ +void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high); +/* ACK/CTS Timeouts */ +extern int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout); +extern unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah); +extern int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout); +extern unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah); +/* Key table (WEP) functions */ +extern int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry); +extern int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry); +extern int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, const struct ieee80211_key_conf *key, const u8 *mac); +extern int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac); + +/* Queue Control Unit, DFS Control Unit Functions */ +extern int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, struct ath5k_txq_info *queue_info); +extern int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, + const struct ath5k_txq_info *queue_info); +extern int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, + enum ath5k_tx_queue queue_type, + struct ath5k_txq_info *queue_info); +extern u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue); +extern void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue); +extern int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue); +extern unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah); +extern int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time); + +/* Hardware Descriptor Functions */ +extern int ath5k_hw_init_desc_functions(struct ath5k_hw *ah); + +/* GPIO Functions */ +extern void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state); +extern int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio); +extern int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio); +extern u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio); +extern int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val); +extern void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio, u32 interrupt_level); + +/* rfkill Functions */ +extern void ath5k_rfkill_hw_start(struct ath5k_hw *ah); +extern void ath5k_rfkill_hw_stop(struct ath5k_hw *ah); + +/* Misc functions */ +int ath5k_hw_set_capabilities(struct ath5k_hw *ah); +extern int ath5k_hw_get_capability(struct ath5k_hw *ah, enum ath5k_capability_type cap_type, u32 capability, u32 *result); +extern int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid, u16 assoc_id); +extern int ath5k_hw_disable_pspoll(struct ath5k_hw *ah); + +/* Initial register settings functions */ +extern int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel); + +/* Initialize RF */ +extern int ath5k_hw_rfregs_init(struct ath5k_hw *ah, + struct ieee80211_channel *channel, + unsigned int mode); +extern int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq); +extern enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah); +extern int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah); +/* PHY/RF channel functions */ +extern bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags); +extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel); +/* PHY calibration */ +extern int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel); +extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq); +/* Spur mitigation */ +bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, + struct ieee80211_channel *channel); +void ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah, + struct ieee80211_channel *channel); +/* Misc PHY functions */ +extern u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan); +extern int ath5k_hw_phy_disable(struct ath5k_hw *ah); +/* Antenna control */ +extern void ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode); +extern void ath5k_hw_set_def_antenna(struct ath5k_hw *ah, u8 ant); +extern unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah); +/* TX power setup */ +extern int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 ee_mode, u8 txpower); +extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower); + +/* + * Functions used internaly + */ + +/* + * Translate usec to hw clock units + * TODO: Half/quarter rate + */ +static inline unsigned int ath5k_hw_htoclock(unsigned int usec, bool turbo) +{ + return turbo ? (usec * 80) : (usec * 40); +} + +/* + * Translate hw clock units to usec + * TODO: Half/quarter rate + */ +static inline unsigned int ath5k_hw_clocktoh(unsigned int clock, bool turbo) +{ + return turbo ? (clock / 80) : (clock / 40); +} + +/* + * Read from a register + */ +static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg) +{ + return ioread32(ah->ah_iobase + reg); +} + +/* + * Write to a register + */ +static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg) +{ + iowrite32(val, ah->ah_iobase + reg); +} + +#if defined(_ATH5K_RESET) || defined(_ATH5K_PHY) +/* + * Check if a register write has been completed + */ +static int ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, + u32 val, bool is_set) +{ + int i; + u32 data; + + for (i = AR5K_TUNE_REGISTER_TIMEOUT; i > 0; i--) { + data = ath5k_hw_reg_read(ah, reg); + if (is_set && (data & flag)) + break; + else if ((data & flag) == val) + break; + udelay(15); + } + + return (i <= 0) ? -EAGAIN : 0; +} +#endif + +static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits) +{ + u32 retval = 0, bit, i; + + for (i = 0; i < bits; i++) { + bit = (val >> i) & 1; + retval = (retval << 1) | bit; + } + + return retval; +} + +static inline int ath5k_pad_size(int hdrlen) +{ + return (hdrlen < 24) ? 0 : hdrlen & 3; +} + +#endif diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c new file mode 100644 index 00000000000..c41ef58393e --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +/*************************************\ +* Attach/Detach Functions and helpers * +\*************************************/ + +#include <linux/pci.h> +#include "ath5k.h" +#include "reg.h" +#include "debug.h" +#include "base.h" + +/** + * ath5k_hw_post - Power On Self Test helper function + * + * @ah: The &struct ath5k_hw + */ +static int ath5k_hw_post(struct ath5k_hw *ah) +{ + + static const u32 static_pattern[4] = { + 0x55555555, 0xaaaaaaaa, + 0x66666666, 0x99999999 + }; + static const u16 regs[2] = { AR5K_STA_ID0, AR5K_PHY(8) }; + int i, c; + u16 cur_reg; + u32 var_pattern; + u32 init_val; + u32 cur_val; + + for (c = 0; c < 2; c++) { + + cur_reg = regs[c]; + + /* Save previous value */ + init_val = ath5k_hw_reg_read(ah, cur_reg); + + for (i = 0; i < 256; i++) { + var_pattern = i << 16 | i; + ath5k_hw_reg_write(ah, var_pattern, cur_reg); + cur_val = ath5k_hw_reg_read(ah, cur_reg); + + if (cur_val != var_pattern) { + ATH5K_ERR(ah->ah_sc, "POST Failed !!!\n"); + return -EAGAIN; + } + + /* Found on ndiswrapper dumps */ + var_pattern = 0x0039080f; + ath5k_hw_reg_write(ah, var_pattern, cur_reg); + } + + for (i = 0; i < 4; i++) { + var_pattern = static_pattern[i]; + ath5k_hw_reg_write(ah, var_pattern, cur_reg); + cur_val = ath5k_hw_reg_read(ah, cur_reg); + + if (cur_val != var_pattern) { + ATH5K_ERR(ah->ah_sc, "POST Failed !!!\n"); + return -EAGAIN; + } + + /* Found on ndiswrapper dumps */ + var_pattern = 0x003b080f; + ath5k_hw_reg_write(ah, var_pattern, cur_reg); + } + + /* Restore previous value */ + ath5k_hw_reg_write(ah, init_val, cur_reg); + + } + + return 0; + +} + +/** + * ath5k_hw_attach - Check if hw is supported and init the needed structs + * + * @sc: The &struct ath5k_softc we got from the driver's attach function + * @mac_version: The mac version id (check out ath5k.h) based on pci id + * + * Check if the device is supported, perform a POST and initialize the needed + * structs. Returns -ENOMEM if we don't have memory for the needed structs, + * -ENODEV if the device is not supported or prints an error msg if something + * else went wrong. + */ +struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) +{ + struct ath5k_hw *ah; + struct pci_dev *pdev = sc->pdev; + int ret; + u32 srev; + + /*If we passed the test malloc a ath5k_hw struct*/ + ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL); + if (ah == NULL) { + ret = -ENOMEM; + ATH5K_ERR(sc, "out of memory\n"); + goto err; + } + + ah->ah_sc = sc; + ah->ah_iobase = sc->iobase; + + /* + * HW information + */ + ah->ah_op_mode = NL80211_IFTYPE_STATION; + ah->ah_radar.r_enabled = AR5K_TUNE_RADAR_ALERT; + ah->ah_turbo = false; + ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; + ah->ah_imr = 0; + ah->ah_atim_window = 0; + ah->ah_aifs = AR5K_TUNE_AIFS; + ah->ah_cw_min = AR5K_TUNE_CWMIN; + ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY; + ah->ah_software_retry = false; + + /* + * Set the mac version based on the pci id + */ + ah->ah_version = mac_version; + + /*Fill the ath5k_hw struct with the needed functions*/ + ret = ath5k_hw_init_desc_functions(ah); + if (ret) + goto err_free; + + /* Bring device out of sleep and reset it's units */ + ret = ath5k_hw_nic_wakeup(ah, CHANNEL_B, true); + if (ret) + goto err_free; + + /* Get MAC, PHY and RADIO revisions */ + srev = ath5k_hw_reg_read(ah, AR5K_SREV); + ah->ah_mac_srev = srev; + ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER); + ah->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV); + ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) & + 0xffffffff; + ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah, + CHANNEL_5GHZ); + ah->ah_phy = AR5K_PHY(0); + + /* Try to identify radio chip based on it's srev */ + switch (ah->ah_radio_5ghz_revision & 0xf0) { + case AR5K_SREV_RAD_5111: + ah->ah_radio = AR5K_RF5111; + ah->ah_single_chip = false; + ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah, + CHANNEL_2GHZ); + break; + case AR5K_SREV_RAD_5112: + case AR5K_SREV_RAD_2112: + ah->ah_radio = AR5K_RF5112; + ah->ah_single_chip = false; + ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah, + CHANNEL_2GHZ); + break; + case AR5K_SREV_RAD_2413: + ah->ah_radio = AR5K_RF2413; + ah->ah_single_chip = true; + break; + case AR5K_SREV_RAD_5413: + ah->ah_radio = AR5K_RF5413; + ah->ah_single_chip = true; + break; + case AR5K_SREV_RAD_2316: + ah->ah_radio = AR5K_RF2316; + ah->ah_single_chip = true; + break; + case AR5K_SREV_RAD_2317: + ah->ah_radio = AR5K_RF2317; + ah->ah_single_chip = true; + break; + case AR5K_SREV_RAD_5424: + if (ah->ah_mac_version == AR5K_SREV_AR2425 || + ah->ah_mac_version == AR5K_SREV_AR2417){ + ah->ah_radio = AR5K_RF2425; + ah->ah_single_chip = true; + } else { + ah->ah_radio = AR5K_RF5413; + ah->ah_single_chip = true; + } + break; + default: + /* Identify radio based on mac/phy srev */ + if (ah->ah_version == AR5K_AR5210) { + ah->ah_radio = AR5K_RF5110; + ah->ah_single_chip = false; + } else if (ah->ah_version == AR5K_AR5211) { + ah->ah_radio = AR5K_RF5111; + ah->ah_single_chip = false; + ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah, + CHANNEL_2GHZ); + } else if (ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4) || + ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4) || + ah->ah_phy_revision == AR5K_SREV_PHY_2425) { + ah->ah_radio = AR5K_RF2425; + ah->ah_single_chip = true; + ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2425; + } else if (srev == AR5K_SREV_AR5213A && + ah->ah_phy_revision == AR5K_SREV_PHY_5212B) { + ah->ah_radio = AR5K_RF5112; + ah->ah_single_chip = false; + ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5112B; + } else if (ah->ah_mac_version == (AR5K_SREV_AR2415 >> 4)) { + ah->ah_radio = AR5K_RF2316; + ah->ah_single_chip = true; + ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2316; + } else if (ah->ah_mac_version == (AR5K_SREV_AR5414 >> 4) || + ah->ah_phy_revision == AR5K_SREV_PHY_5413) { + ah->ah_radio = AR5K_RF5413; + ah->ah_single_chip = true; + ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5413; + } else if (ah->ah_mac_version == (AR5K_SREV_AR2414 >> 4) || + ah->ah_phy_revision == AR5K_SREV_PHY_2413) { + ah->ah_radio = AR5K_RF2413; + ah->ah_single_chip = true; + ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2413; + } else { + ATH5K_ERR(sc, "Couldn't identify radio revision.\n"); + ret = -ENODEV; + goto err_free; + } + } + + + /* Return on unsuported chips (unsupported eeprom etc) */ + if ((srev >= AR5K_SREV_AR5416) && + (srev < AR5K_SREV_AR2425)) { + ATH5K_ERR(sc, "Device not yet supported.\n"); + ret = -ENODEV; + goto err_free; + } + + /* + * Write PCI-E power save settings + */ + if ((ah->ah_version == AR5K_AR5212) && (pdev->is_pcie)) { + ath5k_hw_reg_write(ah, 0x9248fc00, AR5K_PCIE_SERDES); + ath5k_hw_reg_write(ah, 0x24924924, AR5K_PCIE_SERDES); + /* Shut off RX when elecidle is asserted */ + ath5k_hw_reg_write(ah, 0x28000039, AR5K_PCIE_SERDES); + ath5k_hw_reg_write(ah, 0x53160824, AR5K_PCIE_SERDES); + /* TODO: EEPROM work */ + ath5k_hw_reg_write(ah, 0xe5980579, AR5K_PCIE_SERDES); + /* Shut off PLL and CLKREQ active in L1 */ + ath5k_hw_reg_write(ah, 0x001defff, AR5K_PCIE_SERDES); + /* Preserce other settings */ + ath5k_hw_reg_write(ah, 0x1aaabe40, AR5K_PCIE_SERDES); + ath5k_hw_reg_write(ah, 0xbe105554, AR5K_PCIE_SERDES); + ath5k_hw_reg_write(ah, 0x000e3007, AR5K_PCIE_SERDES); + /* Reset SERDES to load new settings */ + ath5k_hw_reg_write(ah, 0x00000000, AR5K_PCIE_SERDES_RESET); + mdelay(1); + } + + /* + * POST + */ + ret = ath5k_hw_post(ah); + if (ret) + goto err_free; + + /* Enable pci core retry fix on Hainan (5213A) and later chips */ + if (srev >= AR5K_SREV_AR5213A) + ath5k_hw_reg_write(ah, AR5K_PCICFG_RETRY_FIX, AR5K_PCICFG); + + /* + * Get card capabilities, calibration values etc + * TODO: EEPROM work + */ + ret = ath5k_eeprom_init(ah); + if (ret) { + ATH5K_ERR(sc, "unable to init EEPROM\n"); + goto err_free; + } + + /* Get misc capabilities */ + ret = ath5k_hw_set_capabilities(ah); + if (ret) { + ATH5K_ERR(sc, "unable to get device capabilities: 0x%04x\n", + sc->pdev->device); + goto err_free; + } + + if (srev >= AR5K_SREV_AR2414) { + ah->ah_combined_mic = true; + AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE, + AR5K_MISC_MODE_COMBINED_MIC); + } + + /* MAC address is cleared until add_interface */ + ath5k_hw_set_lladdr(ah, (u8[ETH_ALEN]){}); + + /* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */ + memset(ah->ah_bssid, 0xff, ETH_ALEN); + ath5k_hw_set_associd(ah, ah->ah_bssid, 0); + ath5k_hw_set_opmode(ah); + + ath5k_hw_rfgain_opt_init(ah); + + return ah; +err_free: + kfree(ah); +err: + return ERR_PTR(ret); +} + +/** + * ath5k_hw_detach - Free the ath5k_hw struct + * + * @ah: The &struct ath5k_hw + */ +void ath5k_hw_detach(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + + __set_bit(ATH_STAT_INVALID, ah->ah_sc->status); + + if (ah->ah_rf_banks != NULL) + kfree(ah->ah_rf_banks); + + ath5k_eeprom_detach(ah); + + /* assume interrupts are down */ + kfree(ah); +} diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c new file mode 100644 index 00000000000..55f7de09d13 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -0,0 +1,3176 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2005 Atheros Communications, Inc. + * Copyright (c) 2006 Devicescape Software, Inc. + * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com> + * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/hardirq.h> +#include <linux/if.h> +#include <linux/io.h> +#include <linux/netdevice.h> +#include <linux/cache.h> +#include <linux/pci.h> +#include <linux/ethtool.h> +#include <linux/uaccess.h> + +#include <net/ieee80211_radiotap.h> + +#include <asm/unaligned.h> + +#include "base.h" +#include "reg.h" +#include "debug.h" + +static int ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */ +static int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static int modparam_all_channels; +module_param_named(all_channels, modparam_all_channels, bool, S_IRUGO); +MODULE_PARM_DESC(all_channels, "Expose all channels the device can use."); + + +/******************\ +* Internal defines * +\******************/ + +/* Module info */ +MODULE_AUTHOR("Jiri Slaby"); +MODULE_AUTHOR("Nick Kossifidis"); +MODULE_DESCRIPTION("Support for 5xxx series of Atheros 802.11 wireless LAN cards."); +MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION("0.6.0 (EXPERIMENTAL)"); + + +/* Known PCI ids */ +static const struct pci_device_id ath5k_pci_id_table[] = { + { PCI_VDEVICE(ATHEROS, 0x0207), .driver_data = AR5K_AR5210 }, /* 5210 early */ + { PCI_VDEVICE(ATHEROS, 0x0007), .driver_data = AR5K_AR5210 }, /* 5210 */ + { PCI_VDEVICE(ATHEROS, 0x0011), .driver_data = AR5K_AR5211 }, /* 5311 - this is on AHB bus !*/ + { PCI_VDEVICE(ATHEROS, 0x0012), .driver_data = AR5K_AR5211 }, /* 5211 */ + { PCI_VDEVICE(ATHEROS, 0x0013), .driver_data = AR5K_AR5212 }, /* 5212 */ + { PCI_VDEVICE(3COM_2, 0x0013), .driver_data = AR5K_AR5212 }, /* 3com 5212 */ + { PCI_VDEVICE(3COM, 0x0013), .driver_data = AR5K_AR5212 }, /* 3com 3CRDAG675 5212 */ + { PCI_VDEVICE(ATHEROS, 0x1014), .driver_data = AR5K_AR5212 }, /* IBM minipci 5212 */ + { PCI_VDEVICE(ATHEROS, 0x0014), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x0015), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x0016), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x0017), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x0018), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x0019), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x001a), .driver_data = AR5K_AR5212 }, /* 2413 Griffin-lite */ + { PCI_VDEVICE(ATHEROS, 0x001b), .driver_data = AR5K_AR5212 }, /* 5413 Eagle */ + { PCI_VDEVICE(ATHEROS, 0x001c), .driver_data = AR5K_AR5212 }, /* PCI-E cards */ + { PCI_VDEVICE(ATHEROS, 0x001d), .driver_data = AR5K_AR5212 }, /* 2417 Nala */ + { 0 } +}; +MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table); + +/* Known SREVs */ +static const struct ath5k_srev_name srev_names[] = { + { "5210", AR5K_VERSION_MAC, AR5K_SREV_AR5210 }, + { "5311", AR5K_VERSION_MAC, AR5K_SREV_AR5311 }, + { "5311A", AR5K_VERSION_MAC, AR5K_SREV_AR5311A }, + { "5311B", AR5K_VERSION_MAC, AR5K_SREV_AR5311B }, + { "5211", AR5K_VERSION_MAC, AR5K_SREV_AR5211 }, + { "5212", AR5K_VERSION_MAC, AR5K_SREV_AR5212 }, + { "5213", AR5K_VERSION_MAC, AR5K_SREV_AR5213 }, + { "5213A", AR5K_VERSION_MAC, AR5K_SREV_AR5213A }, + { "2413", AR5K_VERSION_MAC, AR5K_SREV_AR2413 }, + { "2414", AR5K_VERSION_MAC, AR5K_SREV_AR2414 }, + { "5424", AR5K_VERSION_MAC, AR5K_SREV_AR5424 }, + { "5413", AR5K_VERSION_MAC, AR5K_SREV_AR5413 }, + { "5414", AR5K_VERSION_MAC, AR5K_SREV_AR5414 }, + { "2415", AR5K_VERSION_MAC, AR5K_SREV_AR2415 }, + { "5416", AR5K_VERSION_MAC, AR5K_SREV_AR5416 }, + { "5418", AR5K_VERSION_MAC, AR5K_SREV_AR5418 }, + { "2425", AR5K_VERSION_MAC, AR5K_SREV_AR2425 }, + { "2417", AR5K_VERSION_MAC, AR5K_SREV_AR2417 }, + { "xxxxx", AR5K_VERSION_MAC, AR5K_SREV_UNKNOWN }, + { "5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110 }, + { "5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111 }, + { "5111A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111A }, + { "2111", AR5K_VERSION_RAD, AR5K_SREV_RAD_2111 }, + { "5112", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112 }, + { "5112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A }, + { "5112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112B }, + { "2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112 }, + { "2112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A }, + { "2112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112B }, + { "2413", AR5K_VERSION_RAD, AR5K_SREV_RAD_2413 }, + { "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 }, + { "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 }, + { "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 }, + { "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 }, + { "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 }, + { "xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN }, +}; + +static const struct ieee80211_rate ath5k_rates[] = { + { .bitrate = 10, + .hw_value = ATH5K_RATE_CODE_1M, }, + { .bitrate = 20, + .hw_value = ATH5K_RATE_CODE_2M, + .hw_value_short = ATH5K_RATE_CODE_2M | AR5K_SET_SHORT_PREAMBLE, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, + .hw_value = ATH5K_RATE_CODE_5_5M, + .hw_value_short = ATH5K_RATE_CODE_5_5M | AR5K_SET_SHORT_PREAMBLE, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, + .hw_value = ATH5K_RATE_CODE_11M, + .hw_value_short = ATH5K_RATE_CODE_11M | AR5K_SET_SHORT_PREAMBLE, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60, + .hw_value = ATH5K_RATE_CODE_6M, + .flags = 0 }, + { .bitrate = 90, + .hw_value = ATH5K_RATE_CODE_9M, + .flags = 0 }, + { .bitrate = 120, + .hw_value = ATH5K_RATE_CODE_12M, + .flags = 0 }, + { .bitrate = 180, + .hw_value = ATH5K_RATE_CODE_18M, + .flags = 0 }, + { .bitrate = 240, + .hw_value = ATH5K_RATE_CODE_24M, + .flags = 0 }, + { .bitrate = 360, + .hw_value = ATH5K_RATE_CODE_36M, + .flags = 0 }, + { .bitrate = 480, + .hw_value = ATH5K_RATE_CODE_48M, + .flags = 0 }, + { .bitrate = 540, + .hw_value = ATH5K_RATE_CODE_54M, + .flags = 0 }, + /* XR missing */ +}; + +/* + * Prototypes - PCI stack related functions + */ +static int __devinit ath5k_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id); +static void __devexit ath5k_pci_remove(struct pci_dev *pdev); +#ifdef CONFIG_PM +static int ath5k_pci_suspend(struct pci_dev *pdev, + pm_message_t state); +static int ath5k_pci_resume(struct pci_dev *pdev); +#else +#define ath5k_pci_suspend NULL +#define ath5k_pci_resume NULL +#endif /* CONFIG_PM */ + +static struct pci_driver ath5k_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = ath5k_pci_id_table, + .probe = ath5k_pci_probe, + .remove = __devexit_p(ath5k_pci_remove), + .suspend = ath5k_pci_suspend, + .resume = ath5k_pci_resume, +}; + + + +/* + * Prototypes - MAC 802.11 stack related functions + */ +static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan); +static int ath5k_reset_wake(struct ath5k_softc *sc); +static int ath5k_start(struct ieee80211_hw *hw); +static void ath5k_stop(struct ieee80211_hw *hw); +static int ath5k_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf); +static void ath5k_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf); +static int ath5k_config(struct ieee80211_hw *hw, u32 changed); +static void ath5k_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *new_flags, + int mc_count, struct dev_mc_list *mclist); +static int ath5k_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +static int ath5k_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats); +static int ath5k_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats); +static u64 ath5k_get_tsf(struct ieee80211_hw *hw); +static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf); +static void ath5k_reset_tsf(struct ieee80211_hw *hw); +static int ath5k_beacon_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +static void ath5k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes); + +static const struct ieee80211_ops ath5k_hw_ops = { + .tx = ath5k_tx, + .start = ath5k_start, + .stop = ath5k_stop, + .add_interface = ath5k_add_interface, + .remove_interface = ath5k_remove_interface, + .config = ath5k_config, + .configure_filter = ath5k_configure_filter, + .set_key = ath5k_set_key, + .get_stats = ath5k_get_stats, + .conf_tx = NULL, + .get_tx_stats = ath5k_get_tx_stats, + .get_tsf = ath5k_get_tsf, + .set_tsf = ath5k_set_tsf, + .reset_tsf = ath5k_reset_tsf, + .bss_info_changed = ath5k_bss_info_changed, +}; + +/* + * Prototypes - Internal functions + */ +/* Attach detach */ +static int ath5k_attach(struct pci_dev *pdev, + struct ieee80211_hw *hw); +static void ath5k_detach(struct pci_dev *pdev, + struct ieee80211_hw *hw); +/* Channel/mode setup */ +static inline short ath5k_ieee2mhz(short chan); +static unsigned int ath5k_copy_channels(struct ath5k_hw *ah, + struct ieee80211_channel *channels, + unsigned int mode, + unsigned int max); +static int ath5k_setup_bands(struct ieee80211_hw *hw); +static int ath5k_chan_set(struct ath5k_softc *sc, + struct ieee80211_channel *chan); +static void ath5k_setcurmode(struct ath5k_softc *sc, + unsigned int mode); +static void ath5k_mode_setup(struct ath5k_softc *sc); + +/* Descriptor setup */ +static int ath5k_desc_alloc(struct ath5k_softc *sc, + struct pci_dev *pdev); +static void ath5k_desc_free(struct ath5k_softc *sc, + struct pci_dev *pdev); +/* Buffers setup */ +static int ath5k_rxbuf_setup(struct ath5k_softc *sc, + struct ath5k_buf *bf); +static int ath5k_txbuf_setup(struct ath5k_softc *sc, + struct ath5k_buf *bf); +static inline void ath5k_txbuf_free(struct ath5k_softc *sc, + struct ath5k_buf *bf) +{ + BUG_ON(!bf); + if (!bf->skb) + return; + pci_unmap_single(sc->pdev, bf->skbaddr, bf->skb->len, + PCI_DMA_TODEVICE); + dev_kfree_skb_any(bf->skb); + bf->skb = NULL; +} + +static inline void ath5k_rxbuf_free(struct ath5k_softc *sc, + struct ath5k_buf *bf) +{ + BUG_ON(!bf); + if (!bf->skb) + return; + pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(bf->skb); + bf->skb = NULL; +} + + +/* Queues setup */ +static struct ath5k_txq *ath5k_txq_setup(struct ath5k_softc *sc, + int qtype, int subtype); +static int ath5k_beaconq_setup(struct ath5k_hw *ah); +static int ath5k_beaconq_config(struct ath5k_softc *sc); +static void ath5k_txq_drainq(struct ath5k_softc *sc, + struct ath5k_txq *txq); +static void ath5k_txq_cleanup(struct ath5k_softc *sc); +static void ath5k_txq_release(struct ath5k_softc *sc); +/* Rx handling */ +static int ath5k_rx_start(struct ath5k_softc *sc); +static void ath5k_rx_stop(struct ath5k_softc *sc); +static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc, + struct ath5k_desc *ds, + struct sk_buff *skb, + struct ath5k_rx_status *rs); +static void ath5k_tasklet_rx(unsigned long data); +/* Tx handling */ +static void ath5k_tx_processq(struct ath5k_softc *sc, + struct ath5k_txq *txq); +static void ath5k_tasklet_tx(unsigned long data); +/* Beacon handling */ +static int ath5k_beacon_setup(struct ath5k_softc *sc, + struct ath5k_buf *bf); +static void ath5k_beacon_send(struct ath5k_softc *sc); +static void ath5k_beacon_config(struct ath5k_softc *sc); +static void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf); +static void ath5k_tasklet_beacon(unsigned long data); + +static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) +{ + u64 tsf = ath5k_hw_get_tsf64(ah); + + if ((tsf & 0x7fff) < rstamp) + tsf -= 0x8000; + + return (tsf & ~0x7fff) | rstamp; +} + +/* Interrupt handling */ +static int ath5k_init(struct ath5k_softc *sc); +static int ath5k_stop_locked(struct ath5k_softc *sc); +static int ath5k_stop_hw(struct ath5k_softc *sc); +static irqreturn_t ath5k_intr(int irq, void *dev_id); +static void ath5k_tasklet_reset(unsigned long data); + +static void ath5k_calibrate(unsigned long data); + +/* + * Module init/exit functions + */ +static int __init +init_ath5k_pci(void) +{ + int ret; + + ath5k_debug_init(); + + ret = pci_register_driver(&ath5k_pci_driver); + if (ret) { + printk(KERN_ERR "ath5k_pci: can't register pci driver\n"); + return ret; + } + + return 0; +} + +static void __exit +exit_ath5k_pci(void) +{ + pci_unregister_driver(&ath5k_pci_driver); + + ath5k_debug_finish(); +} + +module_init(init_ath5k_pci); +module_exit(exit_ath5k_pci); + + +/********************\ +* PCI Initialization * +\********************/ + +static const char * +ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val) +{ + const char *name = "xxxxx"; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(srev_names); i++) { + if (srev_names[i].sr_type != type) + continue; + + if ((val & 0xf0) == srev_names[i].sr_val) + name = srev_names[i].sr_name; + + if ((val & 0xff) == srev_names[i].sr_val) { + name = srev_names[i].sr_name; + break; + } + } + + return name; +} + +static int __devinit +ath5k_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void __iomem *mem; + struct ath5k_softc *sc; + struct ieee80211_hw *hw; + int ret; + u8 csz; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "can't enable device\n"); + goto err; + } + + /* XXX 32-bit addressing only */ + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "32-bit DMA not available\n"); + goto err_dis; + } + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); + if (csz == 0) { + /* + * Linux 2.4.18 (at least) writes the cache line size + * register as a 16-bit wide register which is wrong. + * We must have this setup properly for rx buffer + * DMA to work so force a reasonable value here if it + * comes up zero. + */ + csz = L1_CACHE_BYTES / sizeof(u32); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); + } + /* + * The default setting of latency timer yields poor results, + * set it to the value used by other systems. It may be worth + * tweaking this setting more. + */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* + * Disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_write_config_byte(pdev, 0x41, 0); + + ret = pci_request_region(pdev, 0, "ath5k"); + if (ret) { + dev_err(&pdev->dev, "cannot reserve PCI memory region\n"); + goto err_dis; + } + + mem = pci_iomap(pdev, 0, 0); + if (!mem) { + dev_err(&pdev->dev, "cannot remap PCI memory region\n") ; + ret = -EIO; + goto err_reg; + } + + /* + * Allocate hw (mac80211 main struct) + * and hw->priv (driver private data) + */ + hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops); + if (hw == NULL) { + dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n"); + ret = -ENOMEM; + goto err_map; + } + + dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy)); + + /* Initialize driver private data */ + SET_IEEE80211_DEV(hw, &pdev->dev); + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_NOISE_DBM; + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + + hw->extra_tx_headroom = 2; + hw->channel_change_time = 5000; + sc = hw->priv; + sc->hw = hw; + sc->pdev = pdev; + + ath5k_debug_init_device(sc); + + /* + * Mark the device as detached to avoid processing + * interrupts until setup is complete. + */ + __set_bit(ATH_STAT_INVALID, sc->status); + + sc->iobase = mem; /* So we can unmap it on detach */ + sc->cachelsz = csz * sizeof(u32); /* convert to bytes */ + sc->opmode = NL80211_IFTYPE_STATION; + mutex_init(&sc->lock); + spin_lock_init(&sc->rxbuflock); + spin_lock_init(&sc->txbuflock); + spin_lock_init(&sc->block); + + /* Set private data */ + pci_set_drvdata(pdev, hw); + + /* Setup interrupt handler */ + ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); + if (ret) { + ATH5K_ERR(sc, "request_irq failed\n"); + goto err_free; + } + + /* Initialize device */ + sc->ah = ath5k_hw_attach(sc, id->driver_data); + if (IS_ERR(sc->ah)) { + ret = PTR_ERR(sc->ah); + goto err_irq; + } + + /* set up multi-rate retry capabilities */ + if (sc->ah->ah_version == AR5K_AR5212) { + hw->max_rates = 4; + hw->max_rate_tries = 11; + } + + /* Finish private driver data initialization */ + ret = ath5k_attach(pdev, hw); + if (ret) + goto err_ah; + + ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n", + ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev), + sc->ah->ah_mac_srev, + sc->ah->ah_phy_revision); + + if (!sc->ah->ah_single_chip) { + /* Single chip radio (!RF5111) */ + if (sc->ah->ah_radio_5ghz_revision && + !sc->ah->ah_radio_2ghz_revision) { + /* No 5GHz support -> report 2GHz radio */ + if (!test_bit(AR5K_MODE_11A, + sc->ah->ah_capabilities.cap_mode)) { + ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + /* No 2GHz support (5110 and some + * 5Ghz only cards) -> report 5Ghz radio */ + } else if (!test_bit(AR5K_MODE_11B, + sc->ah->ah_capabilities.cap_mode)) { + ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + /* Multiband radio */ + } else { + ATH5K_INFO(sc, "RF%s multiband radio found" + " (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + } + } + /* Multi chip radio (RF5111 - RF2111) -> + * report both 2GHz/5GHz radios */ + else if (sc->ah->ah_radio_5ghz_revision && + sc->ah->ah_radio_2ghz_revision){ + ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_2ghz_revision), + sc->ah->ah_radio_2ghz_revision); + } + } + + + /* ready to process interrupts */ + __clear_bit(ATH_STAT_INVALID, sc->status); + + return 0; +err_ah: + ath5k_hw_detach(sc->ah); +err_irq: + free_irq(pdev->irq, sc); +err_free: + ieee80211_free_hw(hw); +err_map: + pci_iounmap(pdev, mem); +err_reg: + pci_release_region(pdev, 0); +err_dis: + pci_disable_device(pdev); +err: + return ret; +} + +static void __devexit +ath5k_pci_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath5k_softc *sc = hw->priv; + + ath5k_debug_finish_device(sc); + ath5k_detach(pdev, hw); + ath5k_hw_detach(sc->ah); + free_irq(pdev->irq, sc); + pci_iounmap(pdev, sc->iobase); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + ieee80211_free_hw(hw); +} + +#ifdef CONFIG_PM +static int +ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath5k_softc *sc = hw->priv; + + ath5k_led_off(sc); + + free_irq(pdev->irq, sc); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int +ath5k_pci_resume(struct pci_dev *pdev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath5k_softc *sc = hw->priv; + int err; + + pci_restore_state(pdev); + + err = pci_enable_device(pdev); + if (err) + return err; + + err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); + if (err) { + ATH5K_ERR(sc, "request_irq failed\n"); + goto err_no_irq; + } + + ath5k_led_enable(sc); + return 0; + +err_no_irq: + pci_disable_device(pdev); + return err; +} +#endif /* CONFIG_PM */ + + +/***********************\ +* Driver Initialization * +\***********************/ + +static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ath5k_softc *sc = hw->priv; + struct ath_regulatory *reg = &sc->ah->ah_regulatory; + + return ath_reg_notifier_apply(wiphy, request, reg); +} + +static int +ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + u8 mac[ETH_ALEN] = {}; + int ret; + + ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device); + + /* + * Check if the MAC has multi-rate retry support. + * We do this by trying to setup a fake extended + * descriptor. MAC's that don't have support will + * return false w/o doing anything. MAC's that do + * support it will return true w/o doing anything. + */ + ret = ah->ah_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0); + if (ret < 0) + goto err; + if (ret > 0) + __set_bit(ATH_STAT_MRRETRY, sc->status); + + /* + * Collect the channel list. The 802.11 layer + * is resposible for filtering this list based + * on settings like the phy mode and regulatory + * domain restrictions. + */ + ret = ath5k_setup_bands(hw); + if (ret) { + ATH5K_ERR(sc, "can't get channels\n"); + goto err; + } + + /* NB: setup here so ath5k_rate_update is happy */ + if (test_bit(AR5K_MODE_11A, ah->ah_modes)) + ath5k_setcurmode(sc, AR5K_MODE_11A); + else + ath5k_setcurmode(sc, AR5K_MODE_11B); + + /* + * Allocate tx+rx descriptors and populate the lists. + */ + ret = ath5k_desc_alloc(sc, pdev); + if (ret) { + ATH5K_ERR(sc, "can't allocate descriptors\n"); + goto err; + } + + /* + * Allocate hardware transmit queues: one queue for + * beacon frames and one data queue for each QoS + * priority. Note that hw functions handle reseting + * these queues at the needed time. + */ + ret = ath5k_beaconq_setup(ah); + if (ret < 0) { + ATH5K_ERR(sc, "can't setup a beacon xmit queue\n"); + goto err_desc; + } + sc->bhalq = ret; + + sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK); + if (IS_ERR(sc->txq)) { + ATH5K_ERR(sc, "can't setup xmit queue\n"); + ret = PTR_ERR(sc->txq); + goto err_bhal; + } + + tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc); + tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc); + tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc); + tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc); + setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc); + + ret = ath5k_eeprom_read_mac(ah, mac); + if (ret) { + ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n", + sc->pdev->device); + goto err_queues; + } + + SET_IEEE80211_PERM_ADDR(hw, mac); + /* All MAC address bits matter for ACKs */ + memset(sc->bssidmask, 0xff, ETH_ALEN); + ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); + + ah->ah_regulatory.current_rd = + ah->ah_capabilities.cap_eeprom.ee_regdomain; + ret = ath_regd_init(&ah->ah_regulatory, hw->wiphy, ath5k_reg_notifier); + if (ret) { + ATH5K_ERR(sc, "can't initialize regulatory system\n"); + goto err_queues; + } + + ret = ieee80211_register_hw(hw); + if (ret) { + ATH5K_ERR(sc, "can't register ieee80211 hw\n"); + goto err_queues; + } + + if (!ath_is_world_regd(&sc->ah->ah_regulatory)) + regulatory_hint(hw->wiphy, sc->ah->ah_regulatory.alpha2); + + ath5k_init_leds(sc); + + return 0; +err_queues: + ath5k_txq_release(sc); +err_bhal: + ath5k_hw_release_tx_queue(ah, sc->bhalq); +err_desc: + ath5k_desc_free(sc, pdev); +err: + return ret; +} + +static void +ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + + /* + * NB: the order of these is important: + * o call the 802.11 layer before detaching ath5k_hw to + * insure callbacks into the driver to delete global + * key cache entries can be handled + * o reclaim the tx queue data structures after calling + * the 802.11 layer as we'll get called back to reclaim + * node state and potentially want to use them + * o to cleanup the tx queues the hal is called, so detach + * it last + * XXX: ??? detach ath5k_hw ??? + * Other than that, it's straightforward... + */ + ieee80211_unregister_hw(hw); + ath5k_desc_free(sc, pdev); + ath5k_txq_release(sc); + ath5k_hw_release_tx_queue(sc->ah, sc->bhalq); + ath5k_unregister_leds(sc); + + /* + * NB: can't reclaim these until after ieee80211_ifdetach + * returns because we'll get called back to reclaim node + * state and potentially want to use them. + */ +} + + + + +/********************\ +* Channel/mode setup * +\********************/ + +/* + * Convert IEEE channel number to MHz frequency. + */ +static inline short +ath5k_ieee2mhz(short chan) +{ + if (chan <= 14 || chan >= 27) + return ieee80211chan2mhz(chan); + else + return 2212 + chan * 20; +} + +/* + * Returns true for the channel numbers used without all_channels modparam. + */ +static bool ath5k_is_standard_channel(short chan) +{ + return ((chan <= 14) || + /* UNII 1,2 */ + ((chan & 3) == 0 && chan >= 36 && chan <= 64) || + /* midband */ + ((chan & 3) == 0 && chan >= 100 && chan <= 140) || + /* UNII-3 */ + ((chan & 3) == 1 && chan >= 149 && chan <= 165)); +} + +static unsigned int +ath5k_copy_channels(struct ath5k_hw *ah, + struct ieee80211_channel *channels, + unsigned int mode, + unsigned int max) +{ + unsigned int i, count, size, chfreq, freq, ch; + + if (!test_bit(mode, ah->ah_modes)) + return 0; + + switch (mode) { + case AR5K_MODE_11A: + case AR5K_MODE_11A_TURBO: + /* 1..220, but 2GHz frequencies are filtered by check_channel */ + size = 220 ; + chfreq = CHANNEL_5GHZ; + break; + case AR5K_MODE_11B: + case AR5K_MODE_11G: + case AR5K_MODE_11G_TURBO: + size = 26; + chfreq = CHANNEL_2GHZ; + break; + default: + ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n"); + return 0; + } + + for (i = 0, count = 0; i < size && max > 0; i++) { + ch = i + 1 ; + freq = ath5k_ieee2mhz(ch); + + /* Check if channel is supported by the chipset */ + if (!ath5k_channel_ok(ah, freq, chfreq)) + continue; + + if (!modparam_all_channels && !ath5k_is_standard_channel(ch)) + continue; + + /* Write channel info and increment counter */ + channels[count].center_freq = freq; + channels[count].band = (chfreq == CHANNEL_2GHZ) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + switch (mode) { + case AR5K_MODE_11A: + case AR5K_MODE_11G: + channels[count].hw_value = chfreq | CHANNEL_OFDM; + break; + case AR5K_MODE_11A_TURBO: + case AR5K_MODE_11G_TURBO: + channels[count].hw_value = chfreq | + CHANNEL_OFDM | CHANNEL_TURBO; + break; + case AR5K_MODE_11B: + channels[count].hw_value = CHANNEL_B; + } + + count++; + max--; + } + + return count; +} + +static void +ath5k_setup_rate_idx(struct ath5k_softc *sc, struct ieee80211_supported_band *b) +{ + u8 i; + + for (i = 0; i < AR5K_MAX_RATES; i++) + sc->rate_idx[b->band][i] = -1; + + for (i = 0; i < b->n_bitrates; i++) { + sc->rate_idx[b->band][b->bitrates[i].hw_value] = i; + if (b->bitrates[i].hw_value_short) + sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i; + } +} + +static int +ath5k_setup_bands(struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + struct ieee80211_supported_band *sband; + int max_c, count_c = 0; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS); + max_c = ARRAY_SIZE(sc->channels); + + /* 2GHz band */ + sband = &sc->sbands[IEEE80211_BAND_2GHZ]; + sband->band = IEEE80211_BAND_2GHZ; + sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0]; + + if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) { + /* G mode */ + memcpy(sband->bitrates, &ath5k_rates[0], + sizeof(struct ieee80211_rate) * 12); + sband->n_bitrates = 12; + + sband->channels = sc->channels; + sband->n_channels = ath5k_copy_channels(ah, sband->channels, + AR5K_MODE_11G, max_c); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; + count_c = sband->n_channels; + max_c -= count_c; + } else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) { + /* B mode */ + memcpy(sband->bitrates, &ath5k_rates[0], + sizeof(struct ieee80211_rate) * 4); + sband->n_bitrates = 4; + + /* 5211 only supports B rates and uses 4bit rate codes + * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B) + * fix them up here: + */ + if (ah->ah_version == AR5K_AR5211) { + for (i = 0; i < 4; i++) { + sband->bitrates[i].hw_value = + sband->bitrates[i].hw_value & 0xF; + sband->bitrates[i].hw_value_short = + sband->bitrates[i].hw_value_short & 0xF; + } + } + + sband->channels = sc->channels; + sband->n_channels = ath5k_copy_channels(ah, sband->channels, + AR5K_MODE_11B, max_c); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; + count_c = sband->n_channels; + max_c -= count_c; + } + ath5k_setup_rate_idx(sc, sband); + + /* 5GHz band, A mode */ + if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) { + sband = &sc->sbands[IEEE80211_BAND_5GHZ]; + sband->band = IEEE80211_BAND_5GHZ; + sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0]; + + memcpy(sband->bitrates, &ath5k_rates[4], + sizeof(struct ieee80211_rate) * 8); + sband->n_bitrates = 8; + + sband->channels = &sc->channels[count_c]; + sband->n_channels = ath5k_copy_channels(ah, sband->channels, + AR5K_MODE_11A, max_c); + + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband; + } + ath5k_setup_rate_idx(sc, sband); + + ath5k_debug_dump_bands(sc); + + return 0; +} + +/* + * 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 + * ath5k_init. + * + * Called with sc->lock. + */ +static int +ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan) +{ + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "(%u MHz) -> (%u MHz)\n", + sc->curchan->center_freq, chan->center_freq); + + if (chan->center_freq != sc->curchan->center_freq || + chan->hw_value != sc->curchan->hw_value) { + + /* + * 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. + */ + return ath5k_reset(sc, chan); + } + + return 0; +} + +static void +ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode) +{ + sc->curmode = mode; + + if (mode == AR5K_MODE_11A) { + sc->curband = &sc->sbands[IEEE80211_BAND_5GHZ]; + } else { + sc->curband = &sc->sbands[IEEE80211_BAND_2GHZ]; + } +} + +static void +ath5k_mode_setup(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + u32 rfilt; + + /* configure rx filter */ + rfilt = sc->filter_flags; + ath5k_hw_set_rx_filter(ah, rfilt); + + if (ath5k_hw_hasbssidmask(ah)) + ath5k_hw_set_bssid_mask(ah, sc->bssidmask); + + /* configure operational mode */ + ath5k_hw_set_opmode(ah); + + ath5k_hw_set_mcast_filter(ah, 0, 0); + ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt); +} + +static inline int +ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix) +{ + int rix; + + /* return base rate on errors */ + if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES, + "hw_rix out of bounds: %x\n", hw_rix)) + return 0; + + rix = sc->rate_idx[sc->curband->band][hw_rix]; + if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix)) + rix = 0; + + return rix; +} + +/***************\ +* Buffers setup * +\***************/ + +static +struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr) +{ + struct sk_buff *skb; + unsigned int off; + + /* + * Allocate buffer with headroom_needed space for the + * fake physical layer header at the start. + */ + skb = dev_alloc_skb(sc->rxbufsize + sc->cachelsz - 1); + + if (!skb) { + ATH5K_ERR(sc, "can't alloc skbuff of size %u\n", + sc->rxbufsize + sc->cachelsz - 1); + return NULL; + } + /* + * Cache-line-align. This is important (for the + * 5210 at least) as not doing so causes bogus data + * in rx'd frames. + */ + off = ((unsigned long)skb->data) % sc->cachelsz; + if (off != 0) + skb_reserve(skb, sc->cachelsz - off); + + *skb_addr = pci_map_single(sc->pdev, + skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE); + if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) { + ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__); + dev_kfree_skb(skb); + return NULL; + } + return skb; +} + +static int +ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) +{ + struct ath5k_hw *ah = sc->ah; + struct sk_buff *skb = bf->skb; + struct ath5k_desc *ds; + + if (!skb) { + skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr); + if (!skb) + return -ENOMEM; + bf->skb = skb; + } + + /* + * Setup descriptors. For receive we always terminate + * the descriptor list with a self-linked entry so we'll + * not get overrun under high load (as can happen with a + * 5212 when ANI processing enables PHY error frames). + * + * To insure the last descriptor is self-linked we create + * each descriptor as self-linked and add it to the end. As + * each additional descriptor is added the previous self-linked + * entry is ``fixed'' naturally. This should be safe even + * if DMA is happening. When processing RX interrupts we + * never remove/process the last, self-linked, entry on the + * descriptor list. This insures the hardware always has + * someplace to write a new frame. + */ + ds = bf->desc; + ds->ds_link = bf->daddr; /* link to self */ + ds->ds_data = bf->skbaddr; + ah->ah_setup_rx_desc(ah, ds, + skb_tailroom(skb), /* buffer size */ + 0); + + if (sc->rxlink != NULL) + *sc->rxlink = bf->daddr; + sc->rxlink = &ds->ds_link; + return 0; +} + +static int +ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) +{ + struct ath5k_hw *ah = sc->ah; + struct ath5k_txq *txq = sc->txq; + struct ath5k_desc *ds = bf->desc; + struct sk_buff *skb = bf->skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID; + struct ieee80211_rate *rate; + unsigned int mrr_rate[3], mrr_tries[3]; + int i, ret; + u16 hw_rate; + u16 cts_rate = 0; + u16 duration = 0; + u8 rc_flags; + + flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK; + + /* XXX endianness */ + bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, + PCI_DMA_TODEVICE); + + rate = ieee80211_get_tx_rate(sc->hw, info); + + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + flags |= AR5K_TXDESC_NOACK; + + rc_flags = info->control.rates[0].flags; + hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ? + rate->hw_value_short : rate->hw_value; + + pktlen = skb->len; + + /* FIXME: If we are in g mode and rate is a CCK rate + * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta + * from tx power (value is in dB units already) */ + if (info->control.hw_key) { + keyidx = info->control.hw_key->hw_key_idx; + pktlen += info->control.hw_key->icv_len; + } + if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { + flags |= AR5K_TXDESC_RTSENA; + cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value; + duration = le16_to_cpu(ieee80211_rts_duration(sc->hw, + sc->vif, pktlen, info)); + } + if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + flags |= AR5K_TXDESC_CTSENA; + cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value; + duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw, + sc->vif, pktlen, info)); + } + ret = ah->ah_setup_tx_desc(ah, ds, pktlen, + ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL, + (sc->power_level * 2), + hw_rate, + info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags, + cts_rate, duration); + if (ret) + goto err_unmap; + + memset(mrr_rate, 0, sizeof(mrr_rate)); + memset(mrr_tries, 0, sizeof(mrr_tries)); + for (i = 0; i < 3; i++) { + rate = ieee80211_get_alt_retry_rate(sc->hw, info, i); + if (!rate) + break; + + mrr_rate[i] = rate->hw_value; + mrr_tries[i] = info->control.rates[i + 1].count; + } + + ah->ah_setup_mrr_tx_desc(ah, ds, + mrr_rate[0], mrr_tries[0], + mrr_rate[1], mrr_tries[1], + mrr_rate[2], mrr_tries[2]); + + ds->ds_link = 0; + ds->ds_data = bf->skbaddr; + + spin_lock_bh(&txq->lock); + list_add_tail(&bf->list, &txq->q); + sc->tx_stats[txq->qnum].len++; + if (txq->link == NULL) /* is this first packet? */ + ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr); + else /* no, so only link it */ + *txq->link = bf->daddr; + + txq->link = &ds->ds_link; + ath5k_hw_start_tx_dma(ah, txq->qnum); + mmiowb(); + spin_unlock_bh(&txq->lock); + + return 0; +err_unmap: + pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); + return ret; +} + +/*******************\ +* Descriptors setup * +\*******************/ + +static int +ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev) +{ + struct ath5k_desc *ds; + struct ath5k_buf *bf; + dma_addr_t da; + unsigned int i; + int ret; + + /* allocate descriptors */ + sc->desc_len = sizeof(struct ath5k_desc) * + (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1); + sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr); + if (sc->desc == NULL) { + ATH5K_ERR(sc, "can't allocate descriptors\n"); + ret = -ENOMEM; + goto err; + } + ds = sc->desc; + da = sc->desc_daddr; + ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n", + ds, sc->desc_len, (unsigned long long)sc->desc_daddr); + + bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF, + sizeof(struct ath5k_buf), GFP_KERNEL); + if (bf == NULL) { + ATH5K_ERR(sc, "can't allocate bufptr\n"); + ret = -ENOMEM; + goto err_free; + } + sc->bufptr = bf; + + INIT_LIST_HEAD(&sc->rxbuf); + for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) { + bf->desc = ds; + bf->daddr = da; + list_add_tail(&bf->list, &sc->rxbuf); + } + + INIT_LIST_HEAD(&sc->txbuf); + sc->txbuf_len = ATH_TXBUF; + for (i = 0; i < ATH_TXBUF; i++, bf++, ds++, + da += sizeof(*ds)) { + bf->desc = ds; + bf->daddr = da; + list_add_tail(&bf->list, &sc->txbuf); + } + + /* beacon buffer */ + bf->desc = ds; + bf->daddr = da; + sc->bbuf = bf; + + return 0; +err_free: + pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); +err: + sc->desc = NULL; + return ret; +} + +static void +ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev) +{ + struct ath5k_buf *bf; + + ath5k_txbuf_free(sc, sc->bbuf); + list_for_each_entry(bf, &sc->txbuf, list) + ath5k_txbuf_free(sc, bf); + list_for_each_entry(bf, &sc->rxbuf, list) + ath5k_rxbuf_free(sc, bf); + + /* Free memory associated with all descriptors */ + pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); + + kfree(sc->bufptr); + sc->bufptr = NULL; +} + + + + + +/**************\ +* Queues setup * +\**************/ + +static struct ath5k_txq * +ath5k_txq_setup(struct ath5k_softc *sc, + int qtype, int subtype) +{ + struct ath5k_hw *ah = sc->ah; + struct ath5k_txq *txq; + struct ath5k_txq_info qi = { + .tqi_subtype = subtype, + .tqi_aifs = AR5K_TXQ_USEDEFAULT, + .tqi_cw_min = AR5K_TXQ_USEDEFAULT, + .tqi_cw_max = AR5K_TXQ_USEDEFAULT + }; + int qnum; + + /* + * Enable interrupts only for EOL and DESC conditions. + * We mark tx descriptors to receive a DESC interrupt + * when a tx queue gets deep; otherwise waiting for the + * EOL to reap descriptors. Note that this is done to + * reduce interrupt load and this only defers reaping + * descriptors, never transmitting frames. Aside from + * reducing interrupts this also permits more concurrency. + * The only potential downside is if the tx queue backs + * up in which case the top half of the kernel may backup + * due to a lack of tx descriptors. + */ + qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE | + AR5K_TXQ_FLAG_TXDESCINT_ENABLE; + qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi); + if (qnum < 0) { + /* + * NB: don't print a message, this happens + * normally on parts with too few tx queues + */ + return ERR_PTR(qnum); + } + if (qnum >= ARRAY_SIZE(sc->txqs)) { + ATH5K_ERR(sc, "hw qnum %u out of range, max %tu!\n", + qnum, ARRAY_SIZE(sc->txqs)); + ath5k_hw_release_tx_queue(ah, qnum); + return ERR_PTR(-EINVAL); + } + txq = &sc->txqs[qnum]; + if (!txq->setup) { + txq->qnum = qnum; + txq->link = NULL; + INIT_LIST_HEAD(&txq->q); + spin_lock_init(&txq->lock); + txq->setup = true; + } + return &sc->txqs[qnum]; +} + +static int +ath5k_beaconq_setup(struct ath5k_hw *ah) +{ + struct ath5k_txq_info qi = { + .tqi_aifs = AR5K_TXQ_USEDEFAULT, + .tqi_cw_min = AR5K_TXQ_USEDEFAULT, + .tqi_cw_max = AR5K_TXQ_USEDEFAULT, + /* NB: for dynamic turbo, don't enable any other interrupts */ + .tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE + }; + + return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi); +} + +static int +ath5k_beaconq_config(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + struct ath5k_txq_info qi; + int ret; + + ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi); + if (ret) + return ret; + if (sc->opmode == NL80211_IFTYPE_AP || + sc->opmode == NL80211_IFTYPE_MESH_POINT) { + /* + * Always burst out beacon and CAB traffic + * (aifs = cwmin = cwmax = 0) + */ + qi.tqi_aifs = 0; + qi.tqi_cw_min = 0; + qi.tqi_cw_max = 0; + } else if (sc->opmode == NL80211_IFTYPE_ADHOC) { + /* + * Adhoc mode; backoff between 0 and (2 * cw_min). + */ + qi.tqi_aifs = 0; + qi.tqi_cw_min = 0; + qi.tqi_cw_max = 2 * ah->ah_cw_min; + } + + ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, + "beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n", + qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max); + + ret = ath5k_hw_set_tx_queueprops(ah, sc->bhalq, &qi); + if (ret) { + ATH5K_ERR(sc, "%s: unable to update parameters for beacon " + "hardware queue!\n", __func__); + return ret; + } + + return ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */; +} + +static void +ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq) +{ + struct ath5k_buf *bf, *bf0; + + /* + * NB: this assumes output has been stopped and + * we do not need to block ath5k_tx_tasklet + */ + spin_lock_bh(&txq->lock); + list_for_each_entry_safe(bf, bf0, &txq->q, list) { + ath5k_debug_printtxbuf(sc, bf); + + ath5k_txbuf_free(sc, bf); + + spin_lock_bh(&sc->txbuflock); + sc->tx_stats[txq->qnum].len--; + list_move_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + spin_unlock_bh(&sc->txbuflock); + } + txq->link = NULL; + spin_unlock_bh(&txq->lock); +} + +/* + * Drain the transmit queues and reclaim resources. + */ +static void +ath5k_txq_cleanup(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + unsigned int i; + + /* XXX return value */ + if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) { + /* don't touch the hardware if marked invalid */ + ath5k_hw_stop_tx_dma(ah, sc->bhalq); + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n", + ath5k_hw_get_txdp(ah, sc->bhalq)); + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) + if (sc->txqs[i].setup) { + ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum); + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, " + "link %p\n", + sc->txqs[i].qnum, + ath5k_hw_get_txdp(ah, + sc->txqs[i].qnum), + sc->txqs[i].link); + } + } + ieee80211_wake_queues(sc->hw); /* XXX move to callers */ + + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) + if (sc->txqs[i].setup) + ath5k_txq_drainq(sc, &sc->txqs[i]); +} + +static void +ath5k_txq_release(struct ath5k_softc *sc) +{ + struct ath5k_txq *txq = sc->txqs; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++) + if (txq->setup) { + ath5k_hw_release_tx_queue(sc->ah, txq->qnum); + txq->setup = false; + } +} + + + + +/*************\ +* RX Handling * +\*************/ + +/* + * Enable the receive h/w following a reset. + */ +static int +ath5k_rx_start(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + struct ath5k_buf *bf; + int ret; + + sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->cachelsz); + + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rxbufsize %u\n", + sc->cachelsz, sc->rxbufsize); + + spin_lock_bh(&sc->rxbuflock); + sc->rxlink = NULL; + list_for_each_entry(bf, &sc->rxbuf, list) { + ret = ath5k_rxbuf_setup(sc, bf); + if (ret != 0) { + spin_unlock_bh(&sc->rxbuflock); + goto err; + } + } + bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list); + ath5k_hw_set_rxdp(ah, bf->daddr); + spin_unlock_bh(&sc->rxbuflock); + + ath5k_hw_start_rx_dma(ah); /* enable recv descriptors */ + ath5k_mode_setup(sc); /* set filters, etc. */ + ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */ + + return 0; +err: + return ret; +} + +/* + * Disable the receive h/w in preparation for a reset. + */ +static void +ath5k_rx_stop(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + + ath5k_hw_stop_rx_pcu(ah); /* disable PCU */ + ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */ + ath5k_hw_stop_rx_dma(ah); /* disable DMA engine */ + + ath5k_debug_printrxbuffs(sc, ah); + + sc->rxlink = NULL; /* just in case */ +} + +static unsigned int +ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds, + struct sk_buff *skb, struct ath5k_rx_status *rs) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + unsigned int keyix, hlen; + + if (!(rs->rs_status & AR5K_RXERR_DECRYPT) && + rs->rs_keyix != AR5K_RXKEYIX_INVALID) + return RX_FLAG_DECRYPTED; + + /* Apparently when a default key is used to decrypt the packet + the hw does not set the index used to decrypt. In such cases + get the index from the packet. */ + hlen = ieee80211_hdrlen(hdr->frame_control); + if (ieee80211_has_protected(hdr->frame_control) && + !(rs->rs_status & AR5K_RXERR_DECRYPT) && + skb->len >= hlen + 4) { + keyix = skb->data[hlen + 3] >> 6; + + if (test_bit(keyix, sc->keymap)) + return RX_FLAG_DECRYPTED; + } + + return 0; +} + + +static void +ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb, + struct ieee80211_rx_status *rxs) +{ + u64 tsf, bc_tstamp; + u32 hw_tu; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + + if (ieee80211_is_beacon(mgmt->frame_control) && + le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS && + memcmp(mgmt->bssid, sc->ah->ah_bssid, ETH_ALEN) == 0) { + /* + * Received an IBSS beacon with the same BSSID. Hardware *must* + * have updated the local TSF. We have to work around various + * hardware bugs, though... + */ + tsf = ath5k_hw_get_tsf64(sc->ah); + bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp); + hw_tu = TSF_TO_TU(tsf); + + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "beacon %llx mactime %llx (diff %lld) tsf now %llx\n", + (unsigned long long)bc_tstamp, + (unsigned long long)rxs->mactime, + (unsigned long long)(rxs->mactime - bc_tstamp), + (unsigned long long)tsf); + + /* + * Sometimes the HW will give us a wrong tstamp in the rx + * status, causing the timestamp extension to go wrong. + * (This seems to happen especially with beacon frames bigger + * than 78 byte (incl. FCS)) + * But we know that the receive timestamp must be later than the + * timestamp of the beacon since HW must have synced to that. + * + * NOTE: here we assume mactime to be after the frame was + * received, not like mac80211 which defines it at the start. + */ + if (bc_tstamp > rxs->mactime) { + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "fixing mactime from %llx to %llx\n", + (unsigned long long)rxs->mactime, + (unsigned long long)tsf); + rxs->mactime = tsf; + } + + /* + * Local TSF might have moved higher than our beacon timers, + * in that case we have to update them to continue sending + * beacons. This also takes care of synchronizing beacon sending + * times with other stations. + */ + if (hw_tu >= sc->nexttbtt) + ath5k_beacon_update_timers(sc, bc_tstamp); + } +} + +static void +ath5k_tasklet_rx(unsigned long data) +{ + struct ieee80211_rx_status rxs = {}; + struct ath5k_rx_status rs = {}; + struct sk_buff *skb, *next_skb; + dma_addr_t next_skb_addr; + struct ath5k_softc *sc = (void *)data; + struct ath5k_buf *bf; + struct ath5k_desc *ds; + int ret; + int hdrlen; + int padsize; + + spin_lock(&sc->rxbuflock); + if (list_empty(&sc->rxbuf)) { + ATH5K_WARN(sc, "empty rx buf pool\n"); + goto unlock; + } + do { + rxs.flag = 0; + + bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list); + BUG_ON(bf->skb == NULL); + skb = bf->skb; + ds = bf->desc; + + /* bail if HW is still using self-linked descriptor */ + if (ath5k_hw_get_rxdp(sc->ah) == bf->daddr) + break; + + ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs); + if (unlikely(ret == -EINPROGRESS)) + break; + else if (unlikely(ret)) { + ATH5K_ERR(sc, "error in processing rx descriptor\n"); + spin_unlock(&sc->rxbuflock); + return; + } + + if (unlikely(rs.rs_more)) { + ATH5K_WARN(sc, "unsupported jumbo\n"); + goto next; + } + + if (unlikely(rs.rs_status)) { + if (rs.rs_status & AR5K_RXERR_PHY) + goto next; + if (rs.rs_status & AR5K_RXERR_DECRYPT) { + /* + * Decrypt error. If the error occurred + * because there was no hardware key, then + * let the frame through so the upper layers + * can process it. This is necessary for 5210 + * parts which have no way to setup a ``clear'' + * key cache entry. + * + * XXX do key cache faulting + */ + if (rs.rs_keyix == AR5K_RXKEYIX_INVALID && + !(rs.rs_status & AR5K_RXERR_CRC)) + goto accept; + } + if (rs.rs_status & AR5K_RXERR_MIC) { + rxs.flag |= RX_FLAG_MMIC_ERROR; + goto accept; + } + + /* let crypto-error packets fall through in MNTR */ + if ((rs.rs_status & + ~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) || + sc->opmode != NL80211_IFTYPE_MONITOR) + goto next; + } +accept: + next_skb = ath5k_rx_skb_alloc(sc, &next_skb_addr); + + /* + * If we can't replace bf->skb with a new skb under memory + * pressure, just skip this packet + */ + if (!next_skb) + goto next; + + pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize, + PCI_DMA_FROMDEVICE); + skb_put(skb, rs.rs_datalen); + + /* The MAC header is padded to have 32-bit boundary if the + * packet payload is non-zero. The general calculation for + * padsize would take into account odd header lengths: + * padsize = (4 - hdrlen % 4) % 4; However, since only + * even-length headers are used, padding can only be 0 or 2 + * bytes and we can optimize this a bit. In addition, we must + * not try to remove padding from short control frames that do + * not have payload. */ + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + padsize = ath5k_pad_size(hdrlen); + if (padsize) { + memmove(skb->data + padsize, skb->data, hdrlen); + skb_pull(skb, padsize); + } + + /* + * always extend the mac timestamp, since this information is + * also needed for proper IBSS merging. + * + * XXX: it might be too late to do it here, since rs_tstamp is + * 15bit only. that means TSF extension has to be done within + * 32768usec (about 32ms). it might be necessary to move this to + * the interrupt handler, like it is done in madwifi. + * + * Unfortunately we don't know when the hardware takes the rx + * timestamp (beginning of phy frame, data frame, end of rx?). + * The only thing we know is that it is hardware specific... + * On AR5213 it seems the rx timestamp is at the end of the + * frame, but i'm not sure. + * + * NOTE: mac80211 defines mactime at the beginning of the first + * data symbol. Since we don't have any time references it's + * impossible to comply to that. This affects IBSS merge only + * right now, so it's not too bad... + */ + rxs.mactime = ath5k_extend_tsf(sc->ah, rs.rs_tstamp); + rxs.flag |= RX_FLAG_TSFT; + + rxs.freq = sc->curchan->center_freq; + rxs.band = sc->curband->band; + + rxs.noise = sc->ah->ah_noise_floor; + rxs.signal = rxs.noise + rs.rs_rssi; + + /* An rssi of 35 indicates you should be able use + * 54 Mbps reliably. A more elaborate scheme can be used + * here but it requires a map of SNR/throughput for each + * possible mode used */ + rxs.qual = rs.rs_rssi * 100 / 35; + + /* rssi can be more than 35 though, anything above that + * should be considered at 100% */ + if (rxs.qual > 100) + rxs.qual = 100; + + rxs.antenna = rs.rs_antenna; + rxs.rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate); + rxs.flag |= ath5k_rx_decrypted(sc, ds, skb, &rs); + + if (rxs.rate_idx >= 0 && rs.rs_rate == + sc->curband->bitrates[rxs.rate_idx].hw_value_short) + rxs.flag |= RX_FLAG_SHORTPRE; + + ath5k_debug_dump_skb(sc, skb, "RX ", 0); + + /* check beacons in IBSS mode */ + if (sc->opmode == NL80211_IFTYPE_ADHOC) + ath5k_check_ibss_tsf(sc, skb, &rxs); + + __ieee80211_rx(sc->hw, skb, &rxs); + + bf->skb = next_skb; + bf->skbaddr = next_skb_addr; +next: + list_move_tail(&bf->list, &sc->rxbuf); + } while (ath5k_rxbuf_setup(sc, bf) == 0); +unlock: + spin_unlock(&sc->rxbuflock); +} + + + + +/*************\ +* TX Handling * +\*************/ + +static void +ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) +{ + struct ath5k_tx_status ts = {}; + struct ath5k_buf *bf, *bf0; + struct ath5k_desc *ds; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + int i, ret; + + spin_lock(&txq->lock); + list_for_each_entry_safe(bf, bf0, &txq->q, list) { + ds = bf->desc; + + ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts); + if (unlikely(ret == -EINPROGRESS)) + break; + else if (unlikely(ret)) { + ATH5K_ERR(sc, "error %d while processing queue %u\n", + ret, txq->qnum); + break; + } + + skb = bf->skb; + info = IEEE80211_SKB_CB(skb); + bf->skb = NULL; + + pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, + PCI_DMA_TODEVICE); + + ieee80211_tx_info_clear_status(info); + for (i = 0; i < 4; i++) { + struct ieee80211_tx_rate *r = + &info->status.rates[i]; + + if (ts.ts_rate[i]) { + r->idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]); + r->count = ts.ts_retry[i]; + } else { + r->idx = -1; + r->count = 0; + } + } + + /* count the successful attempt as well */ + info->status.rates[ts.ts_final_idx].count++; + + if (unlikely(ts.ts_status)) { + sc->ll_stats.dot11ACKFailureCount++; + if (ts.ts_status & AR5K_TXERR_FILT) + info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + } else { + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.ack_signal = ts.ts_rssi; + } + + ieee80211_tx_status(sc->hw, skb); + sc->tx_stats[txq->qnum].count++; + + spin_lock(&sc->txbuflock); + sc->tx_stats[txq->qnum].len--; + list_move_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + spin_unlock(&sc->txbuflock); + } + if (likely(list_empty(&txq->q))) + txq->link = NULL; + spin_unlock(&txq->lock); + if (sc->txbuf_len > ATH_TXBUF / 5) + ieee80211_wake_queues(sc->hw); +} + +static void +ath5k_tasklet_tx(unsigned long data) +{ + struct ath5k_softc *sc = (void *)data; + + ath5k_tx_processq(sc, sc->txq); +} + + +/*****************\ +* Beacon handling * +\*****************/ + +/* + * Setup the beacon frame for transmit. + */ +static int +ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) +{ + struct sk_buff *skb = bf->skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ath5k_hw *ah = sc->ah; + struct ath5k_desc *ds; + int ret = 0; + u8 antenna; + u32 flags; + + bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, + PCI_DMA_TODEVICE); + ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] " + "skbaddr %llx\n", skb, skb->data, skb->len, + (unsigned long long)bf->skbaddr); + if (pci_dma_mapping_error(sc->pdev, bf->skbaddr)) { + ATH5K_ERR(sc, "beacon DMA mapping failed\n"); + return -EIO; + } + + ds = bf->desc; + antenna = ah->ah_tx_ant; + + flags = AR5K_TXDESC_NOACK; + if (sc->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) { + ds->ds_link = bf->daddr; /* self-linked */ + flags |= AR5K_TXDESC_VEOL; + } else + ds->ds_link = 0; + + /* + * If we use multiple antennas on AP and use + * the Sectored AP scenario, switch antenna every + * 4 beacons to make sure everybody hears our AP. + * When a client tries to associate, hw will keep + * track of the tx antenna to be used for this client + * automaticaly, based on ACKed packets. + * + * Note: AP still listens and transmits RTS on the + * default antenna which is supposed to be an omni. + * + * Note2: On sectored scenarios it's possible to have + * multiple antennas (1omni -the default- and 14 sectors) + * so if we choose to actually support this mode we need + * to allow user to set how many antennas we have and tweak + * the code below to send beacons on all of them. + */ + if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP) + antenna = sc->bsent & 4 ? 2 : 1; + + + /* FIXME: If we are in g mode and rate is a CCK rate + * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta + * from tx power (value is in dB units already) */ + ds->ds_data = bf->skbaddr; + ret = ah->ah_setup_tx_desc(ah, ds, skb->len, + ieee80211_get_hdrlen_from_skb(skb), + AR5K_PKT_TYPE_BEACON, (sc->power_level * 2), + ieee80211_get_tx_rate(sc->hw, info)->hw_value, + 1, AR5K_TXKEYIX_INVALID, + antenna, flags, 0, 0); + if (ret) + goto err_unmap; + + return 0; +err_unmap: + pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); + return ret; +} + +static void ath5k_beacon_disable(struct ath5k_softc *sc) +{ + sc->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA); + ath5k_hw_set_imr(sc->ah, sc->imask); + ath5k_hw_stop_tx_dma(sc->ah, sc->bhalq); +} + +/* + * Transmit a beacon frame at SWBA. Dynamic updates to the + * frame contents are done as needed and the slot time is + * also adjusted based on current state. + * + * This is called from software irq context (beacontq or restq + * tasklets) or user context from ath5k_beacon_config. + */ +static void +ath5k_beacon_send(struct ath5k_softc *sc) +{ + struct ath5k_buf *bf = sc->bbuf; + struct ath5k_hw *ah = sc->ah; + + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n"); + + if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION || + sc->opmode == NL80211_IFTYPE_MONITOR)) { + ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL); + return; + } + /* + * Check if the previous beacon has gone out. If + * not don't 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 (unlikely(ath5k_hw_num_tx_pending(ah, sc->bhalq) != 0)) { + sc->bmisscount++; + ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, + "missed %u consecutive beacons\n", sc->bmisscount); + if (sc->bmisscount > 10) { /* NB: 10 is a guess */ + ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, + "stuck beacon time (%u missed)\n", + sc->bmisscount); + tasklet_schedule(&sc->restq); + } + return; + } + if (unlikely(sc->bmisscount != 0)) { + ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, + "resume beacon xmit after %u misses\n", + sc->bmisscount); + sc->bmisscount = 0; + } + + /* + * Stop any current dma and put the new frame on the queue. + * This should never fail since we check above that no frames + * are still pending on the queue. + */ + if (unlikely(ath5k_hw_stop_tx_dma(ah, sc->bhalq))) { + ATH5K_WARN(sc, "beacon queue %u didn't start/stop ?\n", sc->bhalq); + /* NB: hw still stops DMA, so proceed */ + } + + /* refresh the beacon for AP mode */ + if (sc->opmode == NL80211_IFTYPE_AP) + ath5k_beacon_update(sc->hw, sc->vif); + + ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr); + ath5k_hw_start_tx_dma(ah, sc->bhalq); + ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n", + sc->bhalq, (unsigned long long)bf->daddr, bf->desc); + + sc->bsent++; +} + + +/** + * ath5k_beacon_update_timers - update beacon timers + * + * @sc: struct ath5k_softc pointer we are operating on + * @bc_tsf: the timestamp of the beacon. 0 to reset the TSF. -1 to perform a + * beacon timer update based on the current HW TSF. + * + * Calculate the next target beacon transmit time (TBTT) based on the timestamp + * of a received beacon or the current local hardware TSF and write it to the + * beacon timer registers. + * + * This is called in a variety of situations, e.g. when a beacon is received, + * when a TSF update has been detected, but also when an new IBSS is created or + * when we otherwise know we have to update the timers, but we keep it in this + * function to have it all together in one place. + */ +static void +ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf) +{ + struct ath5k_hw *ah = sc->ah; + u32 nexttbtt, intval, hw_tu, bc_tu; + u64 hw_tsf; + + intval = sc->bintval & AR5K_BEACON_PERIOD; + if (WARN_ON(!intval)) + return; + + /* beacon TSF converted to TU */ + bc_tu = TSF_TO_TU(bc_tsf); + + /* current TSF converted to TU */ + hw_tsf = ath5k_hw_get_tsf64(ah); + hw_tu = TSF_TO_TU(hw_tsf); + +#define FUDGE 3 + /* we use FUDGE to make sure the next TBTT is ahead of the current TU */ + if (bc_tsf == -1) { + /* + * no beacons received, called internally. + * just need to refresh timers based on HW TSF. + */ + nexttbtt = roundup(hw_tu + FUDGE, intval); + } else if (bc_tsf == 0) { + /* + * no beacon received, probably called by ath5k_reset_tsf(). + * reset TSF to start with 0. + */ + nexttbtt = intval; + intval |= AR5K_BEACON_RESET_TSF; + } else if (bc_tsf > hw_tsf) { + /* + * beacon received, SW merge happend but HW TSF not yet updated. + * not possible to reconfigure timers yet, but next time we + * receive a beacon with the same BSSID, the hardware will + * automatically update the TSF and then we need to reconfigure + * the timers. + */ + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "need to wait for HW TSF sync\n"); + return; + } else { + /* + * most important case for beacon synchronization between STA. + * + * beacon received and HW TSF has been already updated by HW. + * update next TBTT based on the TSF of the beacon, but make + * sure it is ahead of our local TSF timer. + */ + nexttbtt = bc_tu + roundup(hw_tu + FUDGE - bc_tu, intval); + } +#undef FUDGE + + sc->nexttbtt = nexttbtt; + + intval |= AR5K_BEACON_ENA; + ath5k_hw_init_beacon(ah, nexttbtt, intval); + + /* + * debugging output last in order to preserve the time critical aspect + * of this function + */ + if (bc_tsf == -1) + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "reconfigured timers based on HW TSF\n"); + else if (bc_tsf == 0) + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "reset HW TSF and timers\n"); + else + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "updated timers based on beacon TSF\n"); + + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "bc_tsf %llx hw_tsf %llx bc_tu %u hw_tu %u nexttbtt %u\n", + (unsigned long long) bc_tsf, + (unsigned long long) hw_tsf, bc_tu, hw_tu, nexttbtt); + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "intval %u %s %s\n", + intval & AR5K_BEACON_PERIOD, + intval & AR5K_BEACON_ENA ? "AR5K_BEACON_ENA" : "", + intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : ""); +} + + +/** + * ath5k_beacon_config - Configure the beacon queues and interrupts + * + * @sc: struct ath5k_softc pointer we are operating on + * + * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA + * interrupts to detect TSF updates only. + */ +static void +ath5k_beacon_config(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + unsigned long flags; + + ath5k_hw_set_imr(ah, 0); + sc->bmisscount = 0; + sc->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA); + + if (sc->opmode == NL80211_IFTYPE_ADHOC || + sc->opmode == NL80211_IFTYPE_MESH_POINT || + sc->opmode == NL80211_IFTYPE_AP) { + /* + * In IBSS mode we use a self-linked tx descriptor and let the + * hardware send the beacons automatically. We have to load it + * only once here. + * We use the SWBA interrupt only to keep track of the beacon + * timers in order to detect automatic TSF updates. + */ + ath5k_beaconq_config(sc); + + sc->imask |= AR5K_INT_SWBA; + + if (sc->opmode == NL80211_IFTYPE_ADHOC) { + if (ath5k_hw_hasveol(ah)) { + spin_lock_irqsave(&sc->block, flags); + ath5k_beacon_send(sc); + spin_unlock_irqrestore(&sc->block, flags); + } + } else + ath5k_beacon_update_timers(sc, -1); + } + + ath5k_hw_set_imr(ah, sc->imask); +} + +static void ath5k_tasklet_beacon(unsigned long data) +{ + struct ath5k_softc *sc = (struct ath5k_softc *) data; + + /* + * Software beacon alert--time to send a beacon. + * + * In IBSS mode we use this interrupt just to + * keep track of the next TBTT (target beacon + * transmission time) in order to detect wether + * automatic TSF updates happened. + */ + if (sc->opmode == NL80211_IFTYPE_ADHOC) { + /* XXX: only if VEOL suppported */ + u64 tsf = ath5k_hw_get_tsf64(sc->ah); + sc->nexttbtt += sc->bintval; + ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, + "SWBA nexttbtt: %x hw_tu: %x " + "TSF: %llx\n", + sc->nexttbtt, + TSF_TO_TU(tsf), + (unsigned long long) tsf); + } else { + spin_lock(&sc->block); + ath5k_beacon_send(sc); + spin_unlock(&sc->block); + } +} + + +/********************\ +* Interrupt handling * +\********************/ + +static int +ath5k_init(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + int ret, i; + + mutex_lock(&sc->lock); + + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode); + + /* + * Stop anything previously setup. This is safe + * no matter this is the first time through or not. + */ + ath5k_stop_locked(sc); + + /* + * 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->curchan = sc->hw->conf.channel; + sc->curband = &sc->sbands[sc->curchan->band]; + sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | + AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | + AR5K_INT_FATAL | AR5K_INT_GLOBAL; + ret = ath5k_reset(sc, NULL); + if (ret) + goto done; + + ath5k_rfkill_hw_start(ah); + + /* + * Reset the key cache since some parts do not reset the + * contents on initial power up or resume from suspend. + */ + for (i = 0; i < AR5K_KEYTABLE_SIZE; i++) + ath5k_hw_reset_key(ah, i); + + /* Set ack to be sent at low bit-rates */ + ath5k_hw_set_ack_bitrate_high(ah, false); + + mod_timer(&sc->calib_tim, round_jiffies(jiffies + + msecs_to_jiffies(ath5k_calinterval * 1000))); + + ret = 0; +done: + mmiowb(); + mutex_unlock(&sc->lock); + return ret; +} + +static int +ath5k_stop_locked(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n", + test_bit(ATH_STAT_INVALID, sc->status)); + + /* + * Shutdown the hardware and driver: + * stop output from above + * disable interrupts + * turn off timers + * turn off the radio + * clear transmit machinery + * clear receive machinery + * drain and release tx queues + * reclaim beacon resources + * power down hardware + * + * Note that some of this work is not possible if the + * hardware is gone (invalid). + */ + ieee80211_stop_queues(sc->hw); + + if (!test_bit(ATH_STAT_INVALID, sc->status)) { + ath5k_led_off(sc); + ath5k_hw_set_imr(ah, 0); + synchronize_irq(sc->pdev->irq); + } + ath5k_txq_cleanup(sc); + if (!test_bit(ATH_STAT_INVALID, sc->status)) { + ath5k_rx_stop(sc); + ath5k_hw_phy_disable(ah); + } else + sc->rxlink = NULL; + + return 0; +} + +/* + * Stop the device, grabbing the top-level lock to protect + * against concurrent entry through ath5k_init (which can happen + * if another thread does a system call and the thread doing the + * stop is preempted). + */ +static int +ath5k_stop_hw(struct ath5k_softc *sc) +{ + int ret; + + mutex_lock(&sc->lock); + ret = ath5k_stop_locked(sc); + if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) { + /* + * Set the chip in full sleep mode. Note that we are + * careful to do this only when bringing the interface + * completely to a stop. When the chip is in this state + * it must be carefully woken up or references to + * registers in the PCI clock domain may freeze the bus + * (and system). This varies by chip and is mostly an + * issue with newer parts that go to sleep more quickly. + */ + if (sc->ah->ah_mac_srev >= 0x78) { + /* + * XXX + * don't put newer MAC revisions > 7.8 to sleep because + * of the above mentioned problems + */ + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mac version > 7.8, " + "not putting device to sleep\n"); + } else { + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, + "putting device to full sleep\n"); + ath5k_hw_set_power(sc->ah, AR5K_PM_FULL_SLEEP, true, 0); + } + } + ath5k_txbuf_free(sc, sc->bbuf); + + mmiowb(); + mutex_unlock(&sc->lock); + + del_timer_sync(&sc->calib_tim); + tasklet_kill(&sc->rxtq); + tasklet_kill(&sc->txtq); + tasklet_kill(&sc->restq); + tasklet_kill(&sc->beacontq); + + ath5k_rfkill_hw_stop(sc->ah); + + return ret; +} + +static irqreturn_t +ath5k_intr(int irq, void *dev_id) +{ + struct ath5k_softc *sc = dev_id; + struct ath5k_hw *ah = sc->ah; + enum ath5k_int status; + unsigned int counter = 1000; + + if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) || + !ath5k_hw_is_intr_pending(ah))) + return IRQ_NONE; + + do { + ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */ + ATH5K_DBG(sc, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n", + status, sc->imask); + if (unlikely(status & AR5K_INT_FATAL)) { + /* + * Fatal errors are unrecoverable. + * Typically these are caused by DMA errors. + */ + tasklet_schedule(&sc->restq); + } else if (unlikely(status & AR5K_INT_RXORN)) { + tasklet_schedule(&sc->restq); + } else { + if (status & AR5K_INT_SWBA) { + tasklet_hi_schedule(&sc->beacontq); + } + if (status & AR5K_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. + */ + sc->rxlink = NULL; + } + if (status & AR5K_INT_TXURN) { + /* bump tx trigger level */ + ath5k_hw_update_tx_triglevel(ah, true); + } + if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR)) + tasklet_schedule(&sc->rxtq); + if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC + | AR5K_INT_TXERR | AR5K_INT_TXEOL)) + tasklet_schedule(&sc->txtq); + if (status & AR5K_INT_BMISS) { + /* TODO */ + } + if (status & AR5K_INT_MIB) { + /* + * These stats are also used for ANI i think + * so how about updating them more often ? + */ + ath5k_hw_update_mib_counters(ah, &sc->ll_stats); + } + if (status & AR5K_INT_GPIO) + tasklet_schedule(&sc->rf_kill.toggleq); + + } + } while (ath5k_hw_is_intr_pending(ah) && --counter > 0); + + if (unlikely(!counter)) + ATH5K_WARN(sc, "too many interrupts, giving up for now\n"); + + return IRQ_HANDLED; +} + +static void +ath5k_tasklet_reset(unsigned long data) +{ + struct ath5k_softc *sc = (void *)data; + + ath5k_reset_wake(sc); +} + +/* + * Periodically recalibrate the PHY to account + * for temperature/environment changes. + */ +static void +ath5k_calibrate(unsigned long data) +{ + struct ath5k_softc *sc = (void *)data; + struct ath5k_hw *ah = sc->ah; + + ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n", + ieee80211_frequency_to_channel(sc->curchan->center_freq), + sc->curchan->hw_value); + + if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) { + /* + * Rfgain is out of bounds, reset the chip + * to load new gain values. + */ + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n"); + ath5k_reset_wake(sc); + } + if (ath5k_hw_phy_calibrate(ah, sc->curchan)) + ATH5K_ERR(sc, "calibration of channel %u failed\n", + ieee80211_frequency_to_channel( + sc->curchan->center_freq)); + + mod_timer(&sc->calib_tim, round_jiffies(jiffies + + msecs_to_jiffies(ath5k_calinterval * 1000))); +} + + +/********************\ +* Mac80211 functions * +\********************/ + +static int +ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_buf *bf; + unsigned long flags; + int hdrlen; + int padsize; + + ath5k_debug_dump_skb(sc, skb, "TX ", 1); + + if (sc->opmode == NL80211_IFTYPE_MONITOR) + ATH5K_DBG(sc, ATH5K_DEBUG_XMIT, "tx in monitor (scan?)\n"); + + /* + * the hardware expects the header padded to 4 byte boundaries + * if this is not the case we add the padding after the header + */ + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + padsize = ath5k_pad_size(hdrlen); + if (padsize) { + + if (skb_headroom(skb) < padsize) { + ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough" + " headroom to pad %d\n", hdrlen, padsize); + goto drop_packet; + } + skb_push(skb, padsize); + memmove(skb->data, skb->data+padsize, hdrlen); + } + + spin_lock_irqsave(&sc->txbuflock, flags); + if (list_empty(&sc->txbuf)) { + ATH5K_ERR(sc, "no further txbuf available, dropping packet\n"); + spin_unlock_irqrestore(&sc->txbuflock, flags); + ieee80211_stop_queue(hw, skb_get_queue_mapping(skb)); + goto drop_packet; + } + bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list); + list_del(&bf->list); + sc->txbuf_len--; + if (list_empty(&sc->txbuf)) + ieee80211_stop_queues(hw); + spin_unlock_irqrestore(&sc->txbuflock, flags); + + bf->skb = skb; + + if (ath5k_txbuf_setup(sc, bf)) { + bf->skb = NULL; + spin_lock_irqsave(&sc->txbuflock, flags); + list_add_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + spin_unlock_irqrestore(&sc->txbuflock, flags); + goto drop_packet; + } + return NETDEV_TX_OK; + +drop_packet: + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +/* + * Reset the hardware. If chan is not NULL, then also pause rx/tx + * and change to the given channel. + */ +static int +ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan) +{ + struct ath5k_hw *ah = sc->ah; + int ret; + + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n"); + + if (chan) { + ath5k_hw_set_imr(ah, 0); + ath5k_txq_cleanup(sc); + ath5k_rx_stop(sc); + + sc->curchan = chan; + sc->curband = &sc->sbands[chan->band]; + } + ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, true); + if (ret) { + ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret); + goto err; + } + + ret = ath5k_rx_start(sc); + if (ret) { + ATH5K_ERR(sc, "can't start recv logic\n"); + goto err; + } + + /* + * Change channels and update the h/w rate map if we're switching; + * e.g. 11a to 11b/g. + * + * We may be doing a reset in response to an ioctl that changes the + * channel so update any state that might change as a result. + * + * XXX needed? + */ +/* ath5k_chan_change(sc, c); */ + + ath5k_beacon_config(sc); + /* intrs are enabled by ath5k_beacon_config */ + + return 0; +err: + return ret; +} + +static int +ath5k_reset_wake(struct ath5k_softc *sc) +{ + int ret; + + ret = ath5k_reset(sc, sc->curchan); + if (!ret) + ieee80211_wake_queues(sc->hw); + + return ret; +} + +static int ath5k_start(struct ieee80211_hw *hw) +{ + return ath5k_init(hw->priv); +} + +static void ath5k_stop(struct ieee80211_hw *hw) +{ + ath5k_stop_hw(hw->priv); +} + +static int ath5k_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ath5k_softc *sc = hw->priv; + int ret; + + mutex_lock(&sc->lock); + if (sc->vif) { + ret = 0; + goto end; + } + + sc->vif = conf->vif; + + switch (conf->type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_MONITOR: + sc->opmode = conf->type; + break; + default: + ret = -EOPNOTSUPP; + goto end; + } + + /* Set to a reasonable value. Note that this will + * be set to mac80211's value at ath5k_config(). */ + sc->bintval = 1000; + ath5k_hw_set_lladdr(sc->ah, conf->mac_addr); + + ret = 0; +end: + mutex_unlock(&sc->lock); + return ret; +} + +static void +ath5k_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ath5k_softc *sc = hw->priv; + u8 mac[ETH_ALEN] = {}; + + mutex_lock(&sc->lock); + if (sc->vif != conf->vif) + goto end; + + ath5k_hw_set_lladdr(sc->ah, mac); + ath5k_beacon_disable(sc); + sc->vif = NULL; +end: + mutex_unlock(&sc->lock); +} + +/* + * TODO: Phy disable/diversity etc + */ +static int +ath5k_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + struct ieee80211_conf *conf = &hw->conf; + int ret = 0; + + mutex_lock(&sc->lock); + + ret = ath5k_chan_set(sc, conf->channel); + if (ret < 0) + goto unlock; + + if ((changed & IEEE80211_CONF_CHANGE_POWER) && + (sc->power_level != conf->power_level)) { + sc->power_level = conf->power_level; + + /* Half dB steps */ + ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2)); + } + + /* TODO: + * 1) Move this on config_interface and handle each case + * separately eg. when we have only one STA vif, use + * AR5K_ANTMODE_SINGLE_AP + * + * 2) Allow the user to change antenna mode eg. when only + * one antenna is present + * + * 3) Allow the user to set default/tx antenna when possible + * + * 4) Default mode should handle 90% of the cases, together + * with fixed a/b and single AP modes we should be able to + * handle 99%. Sectored modes are extreme cases and i still + * haven't found a usage for them. If we decide to support them, + * then we must allow the user to set how many tx antennas we + * have available + */ + ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT); + +unlock: + mutex_unlock(&sc->lock); + return ret; +} + +#define SUPPORTED_FIF_FLAGS \ + FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | \ + FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC +/* + * o always accept unicast, broadcast, and multicast traffic + * o multicast traffic for all BSSIDs will be enabled if mac80211 + * says it should be + * o maintain current state of phy ofdm or phy cck error reception. + * If the hardware detects any of these type of errors then + * ath5k_hw_get_rx_filter() will pass to us the respective + * hardware filters to be able to receive these type of frames. + * o probe request frames are accepted only when operating in + * hostap, adhoc, or monitor modes + * o enable promiscuous mode according to the interface state + * o accept beacons: + * - when operating in adhoc mode so the 802.11 layer creates + * node table entries for peers, + * - when operating in station mode for collecting rssi data when + * the station is otherwise quiet, or + * - when scanning + */ +static void ath5k_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *new_flags, + int mc_count, struct dev_mc_list *mclist) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + u32 mfilt[2], val, rfilt; + u8 pos; + int i; + + mfilt[0] = 0; + mfilt[1] = 0; + + /* Only deal with supported flags */ + changed_flags &= SUPPORTED_FIF_FLAGS; + *new_flags &= SUPPORTED_FIF_FLAGS; + + /* If HW detects any phy or radar errors, leave those filters on. + * Also, always enable Unicast, Broadcasts and Multicast + * XXX: move unicast, bssid broadcasts and multicast to mac80211 */ + rfilt = (ath5k_hw_get_rx_filter(ah) & (AR5K_RX_FILTER_PHYERR)) | + (AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST | + AR5K_RX_FILTER_MCAST); + + if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) { + if (*new_flags & FIF_PROMISC_IN_BSS) { + rfilt |= AR5K_RX_FILTER_PROM; + __set_bit(ATH_STAT_PROMISC, sc->status); + } else { + __clear_bit(ATH_STAT_PROMISC, sc->status); + } + } + + /* Note, AR5K_RX_FILTER_MCAST is already enabled */ + if (*new_flags & FIF_ALLMULTI) { + mfilt[0] = ~0; + mfilt[1] = ~0; + } else { + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + /* calculate XOR of eight 6-bit values */ + val = get_unaligned_le32(mclist->dmi_addr + 0); + pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; + val = get_unaligned_le32(mclist->dmi_addr + 3); + pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; + pos &= 0x3f; + mfilt[pos / 32] |= (1 << (pos % 32)); + /* XXX: we might be able to just do this instead, + * but not sure, needs testing, if we do use this we'd + * neet to inform below to not reset the mcast */ + /* ath5k_hw_set_mcast_filterindex(ah, + * mclist->dmi_addr[5]); */ + mclist = mclist->next; + } + } + + /* This is the best we can do */ + if (*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL)) + rfilt |= AR5K_RX_FILTER_PHYERR; + + /* FIF_BCN_PRBRESP_PROMISC really means to enable beacons + * and probes for any BSSID, this needs testing */ + if (*new_flags & FIF_BCN_PRBRESP_PROMISC) + rfilt |= AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_PROBEREQ; + + /* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not + * set we should only pass on control frames for this + * station. This needs testing. I believe right now this + * enables *all* control frames, which is OK.. but + * but we should see if we can improve on granularity */ + if (*new_flags & FIF_CONTROL) + rfilt |= AR5K_RX_FILTER_CONTROL; + + /* Additional settings per mode -- this is per ath5k */ + + /* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */ + + if (sc->opmode == NL80211_IFTYPE_MONITOR) + rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | + AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM; + if (sc->opmode != NL80211_IFTYPE_STATION) + rfilt |= AR5K_RX_FILTER_PROBEREQ; + if (sc->opmode != NL80211_IFTYPE_AP && + sc->opmode != NL80211_IFTYPE_MESH_POINT && + test_bit(ATH_STAT_PROMISC, sc->status)) + rfilt |= AR5K_RX_FILTER_PROM; + if ((sc->opmode == NL80211_IFTYPE_STATION && sc->assoc) || + sc->opmode == NL80211_IFTYPE_ADHOC || + sc->opmode == NL80211_IFTYPE_AP) + rfilt |= AR5K_RX_FILTER_BEACON; + if (sc->opmode == NL80211_IFTYPE_MESH_POINT) + rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | + AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM; + + /* Set filters */ + ath5k_hw_set_rx_filter(ah, rfilt); + + /* Set multicast bits */ + ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]); + /* Set the cached hw filter flags, this will alter actually + * be set in HW */ + sc->filter_flags = rfilt; +} + +static int +ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath5k_softc *sc = hw->priv; + int ret = 0; + + if (modparam_nohwcrypt) + return -EOPNOTSUPP; + + switch (key->alg) { + case ALG_WEP: + case ALG_TKIP: + break; + case ALG_CCMP: + return -EOPNOTSUPP; + default: + WARN_ON(1); + return -EINVAL; + } + + mutex_lock(&sc->lock); + + switch (cmd) { + case SET_KEY: + ret = ath5k_hw_set_key(sc->ah, key->keyidx, key, + sta ? sta->addr : NULL); + if (ret) { + ATH5K_ERR(sc, "can't set the key\n"); + goto unlock; + } + __set_bit(key->keyidx, sc->keymap); + key->hw_key_idx = key->keyidx; + key->flags |= (IEEE80211_KEY_FLAG_GENERATE_IV | + IEEE80211_KEY_FLAG_GENERATE_MMIC); + break; + case DISABLE_KEY: + ath5k_hw_reset_key(sc->ah, key->keyidx); + __clear_bit(key->keyidx, sc->keymap); + break; + default: + ret = -EINVAL; + goto unlock; + } + +unlock: + mmiowb(); + mutex_unlock(&sc->lock); + return ret; +} + +static int +ath5k_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + + /* Force update */ + ath5k_hw_update_mib_counters(ah, &sc->ll_stats); + + memcpy(stats, &sc->ll_stats, sizeof(sc->ll_stats)); + + return 0; +} + +static int +ath5k_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats) +{ + struct ath5k_softc *sc = hw->priv; + + memcpy(stats, &sc->tx_stats, sizeof(sc->tx_stats)); + + return 0; +} + +static u64 +ath5k_get_tsf(struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + + return ath5k_hw_get_tsf64(sc->ah); +} + +static void +ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +{ + struct ath5k_softc *sc = hw->priv; + + ath5k_hw_set_tsf64(sc->ah, tsf); +} + +static void +ath5k_reset_tsf(struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + + /* + * in IBSS mode we need to update the beacon timers too. + * this will also reset the TSF if we call it with 0 + */ + if (sc->opmode == NL80211_IFTYPE_ADHOC) + ath5k_beacon_update_timers(sc, 0); + else + ath5k_hw_reset_tsf(sc->ah); +} + +/* + * Updates the beacon that is sent by ath5k_beacon_send. For adhoc, + * this is called only once at config_bss time, for AP we do it every + * SWBA interrupt so that the TIM will reflect buffered frames. + * + * Called with the beacon lock. + */ +static int +ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + int ret; + struct ath5k_softc *sc = hw->priv; + struct sk_buff *skb; + + if (WARN_ON(!vif)) { + ret = -EINVAL; + goto out; + } + + skb = ieee80211_beacon_get(hw, vif); + + if (!skb) { + ret = -ENOMEM; + goto out; + } + + ath5k_debug_dump_skb(sc, skb, "BC ", 1); + + ath5k_txbuf_free(sc, sc->bbuf); + sc->bbuf->skb = skb; + ret = ath5k_beacon_setup(sc, sc->bbuf); + if (ret) + sc->bbuf->skb = NULL; +out: + return ret; +} + +/* + * Update the beacon and reconfigure the beacon queues. + */ +static void +ath5k_beacon_reconfig(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + int ret; + unsigned long flags; + struct ath5k_softc *sc = hw->priv; + + spin_lock_irqsave(&sc->block, flags); + ret = ath5k_beacon_update(hw, vif); + spin_unlock_irqrestore(&sc->block, flags); + if (ret == 0) { + ath5k_beacon_config(sc); + mmiowb(); + } +} + +static void +set_beacon_filter(struct ieee80211_hw *hw, bool enable) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + u32 rfilt; + rfilt = ath5k_hw_get_rx_filter(ah); + if (enable) + rfilt |= AR5K_RX_FILTER_BEACON; + else + rfilt &= ~AR5K_RX_FILTER_BEACON; + ath5k_hw_set_rx_filter(ah, rfilt); + sc->filter_flags = rfilt; +} + +static void ath5k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + + mutex_lock(&sc->lock); + if (WARN_ON(sc->vif != vif)) + goto unlock; + + if (changes & BSS_CHANGED_BSSID) { + /* Cache for later use during resets */ + memcpy(ah->ah_bssid, bss_conf->bssid, ETH_ALEN); + /* XXX: assoc id is set to 0 for now, mac80211 doesn't have + * a clean way of letting us retrieve this yet. */ + ath5k_hw_set_associd(ah, ah->ah_bssid, 0); + mmiowb(); + } + + if (changes & BSS_CHANGED_BEACON_INT) + sc->bintval = bss_conf->beacon_int; + + if (changes & BSS_CHANGED_ASSOC) { + sc->assoc = bss_conf->assoc; + if (sc->opmode == NL80211_IFTYPE_STATION) + set_beacon_filter(hw, sc->assoc); + } + + if (changes & BSS_CHANGED_BEACON && + (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT || + vif->type == NL80211_IFTYPE_AP)) { + ath5k_beacon_reconfig(hw, vif); + } + + unlock: + mutex_unlock(&sc->lock); +} diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h new file mode 100644 index 00000000000..f9b7f2f819b --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -0,0 +1,201 @@ +/*- + * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +/* + * Defintions for the Atheros Wireless LAN controller driver. + */ +#ifndef _DEV_ATH_ATHVAR_H +#define _DEV_ATH_ATHVAR_H + +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/wireless.h> +#include <linux/if_ether.h> +#include <linux/leds.h> +#include <linux/rfkill.h> + +#include "ath5k.h" +#include "debug.h" + +#define ATH_RXBUF 40 /* number of RX buffers */ +#define ATH_TXBUF 200 /* number of TX buffers */ +#define ATH_BCBUF 1 /* number of beacon buffers */ + +struct ath5k_buf { + struct list_head list; + struct ath5k_desc *desc; /* virtual addr of desc */ + dma_addr_t daddr; /* physical addr of desc */ + struct sk_buff *skb; /* skbuff for buf */ + dma_addr_t skbaddr;/* physical addr of skb data */ +}; + +/* + * 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 ath5k_txq { + unsigned int qnum; /* hardware q number */ + u32 *link; /* link ptr in last TX desc */ + struct list_head q; /* transmit queue */ + spinlock_t lock; /* lock on q and link */ + bool setup; +}; + +#define ATH5K_LED_MAX_NAME_LEN 31 + +/* + * State for LED triggers + */ +struct ath5k_led +{ + char name[ATH5K_LED_MAX_NAME_LEN + 1]; /* name of the LED in sysfs */ + struct ath5k_softc *sc; /* driver state */ + struct led_classdev led_dev; /* led classdev */ +}; + +/* Rfkill */ +struct ath5k_rfkill { + /* GPIO PIN for rfkill */ + u16 gpio; + /* polarity of rfkill GPIO PIN */ + bool polarity; + /* RFKILL toggle tasklet */ + struct tasklet_struct toggleq; +}; + +#if CHAN_DEBUG +#define ATH_CHAN_MAX (26+26+26+200+200) +#else +#define ATH_CHAN_MAX (14+14+14+252+20) +#endif + +/* Software Carrier, keeps track of the driver state + * associated with an instance of a device */ +struct ath5k_softc { + struct pci_dev *pdev; /* for dma mapping */ + void __iomem *iobase; /* address of the device */ + struct mutex lock; /* dev-level lock */ + /* FIXME: how many does it really need? */ + struct ieee80211_tx_queue_stats tx_stats[16]; + struct ieee80211_low_level_stats ll_stats; + struct ieee80211_hw *hw; /* IEEE 802.11 common */ + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + struct ieee80211_channel channels[ATH_CHAN_MAX]; + struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES]; + s8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES]; + enum nl80211_iftype opmode; + struct ath5k_hw *ah; /* Atheros HW */ + + struct ieee80211_supported_band *curband; + +#ifdef CONFIG_ATH5K_DEBUG + struct ath5k_dbg_info debug; /* debug info */ +#endif /* CONFIG_ATH5K_DEBUG */ + + struct ath5k_buf *bufptr; /* allocated buffer ptr */ + struct ath5k_desc *desc; /* TX/RX descriptors */ + dma_addr_t desc_daddr; /* DMA (physical) address */ + size_t desc_len; /* size of TX/RX descriptors */ + u16 cachelsz; /* cache line size */ + + DECLARE_BITMAP(status, 5); +#define ATH_STAT_INVALID 0 /* disable hardware accesses */ +#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */ +#define ATH_STAT_PROMISC 2 +#define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */ +#define ATH_STAT_STARTED 4 /* opened & irqs enabled */ + + unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ + unsigned int curmode; /* current phy mode */ + struct ieee80211_channel *curchan; /* current h/w channel */ + + struct ieee80211_vif *vif; + + enum ath5k_int imask; /* interrupt mask copy */ + + DECLARE_BITMAP(keymap, AR5K_KEYCACHE_SIZE); /* key use bit map */ + + u8 bssidmask[ETH_ALEN]; + + unsigned int led_pin, /* GPIO pin for driving LED */ + led_on; /* pin setting for LED on */ + + struct tasklet_struct restq; /* reset tasklet */ + + unsigned int rxbufsize; /* rx size based on mtu */ + struct list_head rxbuf; /* receive buffer */ + spinlock_t rxbuflock; + u32 *rxlink; /* link ptr in last RX desc */ + struct tasklet_struct rxtq; /* rx intr tasklet */ + struct ath5k_led rx_led; /* rx led */ + + struct list_head txbuf; /* transmit buffer */ + spinlock_t txbuflock; + unsigned int txbuf_len; /* buf count in txbuf list */ + struct ath5k_txq txqs[2]; /* beacon and tx */ + + struct ath5k_txq *txq; /* beacon and tx*/ + struct tasklet_struct txtq; /* tx intr tasklet */ + struct ath5k_led tx_led; /* tx led */ + + struct ath5k_rfkill rf_kill; + + spinlock_t block; /* protects beacon */ + struct tasklet_struct beacontq; /* beacon intr tasklet */ + struct ath5k_buf *bbuf; /* beacon buffer */ + unsigned int bhalq, /* SW q for outgoing beacons */ + bmisscount, /* missed beacon transmits */ + bintval, /* beacon interval in TU */ + bsent; + unsigned int nexttbtt; /* next beacon time in TU */ + + struct timer_list calib_tim; /* calibration timer */ + int power_level; /* Requested tx power in dbm */ + bool assoc; /* assocate state */ +}; + +#define ath5k_hw_hasbssidmask(_ah) \ + (ath5k_hw_get_capability(_ah, AR5K_CAP_BSSIDMASK, 0, NULL) == 0) +#define ath5k_hw_hasveol(_ah) \ + (ath5k_hw_get_capability(_ah, AR5K_CAP_VEOL, 0, NULL) == 0) + +#endif diff --git a/drivers/net/wireless/ath/ath5k/caps.c b/drivers/net/wireless/ath/ath5k/caps.c new file mode 100644 index 00000000000..367a6c7d3cc --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/caps.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +/**************\ +* Capabilities * +\**************/ + +#include "ath5k.h" +#include "reg.h" +#include "debug.h" +#include "base.h" + +/* + * Fill the capabilities struct + * TODO: Merge this with EEPROM code when we are done with it + */ +int ath5k_hw_set_capabilities(struct ath5k_hw *ah) +{ + u16 ee_header; + + ATH5K_TRACE(ah->ah_sc); + /* Capabilities stored in the EEPROM */ + ee_header = ah->ah_capabilities.cap_eeprom.ee_header; + + if (ah->ah_version == AR5K_AR5210) { + /* + * Set radio capabilities + * (The AR5110 only supports the middle 5GHz band) + */ + ah->ah_capabilities.cap_range.range_5ghz_min = 5120; + ah->ah_capabilities.cap_range.range_5ghz_max = 5430; + ah->ah_capabilities.cap_range.range_2ghz_min = 0; + ah->ah_capabilities.cap_range.range_2ghz_max = 0; + + /* Set supported modes */ + __set_bit(AR5K_MODE_11A, ah->ah_capabilities.cap_mode); + __set_bit(AR5K_MODE_11A_TURBO, ah->ah_capabilities.cap_mode); + } else { + /* + * XXX The tranceiver supports frequencies from 4920 to 6100GHz + * XXX and from 2312 to 2732GHz. There are problems with the + * XXX current ieee80211 implementation because the IEEE + * XXX channel mapping does not support negative channel + * XXX numbers (2312MHz is channel -19). Of course, this + * XXX doesn't matter because these channels are out of range + * XXX but some regulation domains like MKK (Japan) will + * XXX support frequencies somewhere around 4.8GHz. + */ + + /* + * Set radio capabilities + */ + + if (AR5K_EEPROM_HDR_11A(ee_header)) { + /* 4920 */ + ah->ah_capabilities.cap_range.range_5ghz_min = 5005; + ah->ah_capabilities.cap_range.range_5ghz_max = 6100; + + /* Set supported modes */ + __set_bit(AR5K_MODE_11A, + ah->ah_capabilities.cap_mode); + __set_bit(AR5K_MODE_11A_TURBO, + ah->ah_capabilities.cap_mode); + if (ah->ah_version == AR5K_AR5212) + __set_bit(AR5K_MODE_11G_TURBO, + ah->ah_capabilities.cap_mode); + } + + /* Enable 802.11b if a 2GHz capable radio (2111/5112) is + * connected */ + if (AR5K_EEPROM_HDR_11B(ee_header) || + (AR5K_EEPROM_HDR_11G(ee_header) && + ah->ah_version != AR5K_AR5211)) { + /* 2312 */ + ah->ah_capabilities.cap_range.range_2ghz_min = 2412; + ah->ah_capabilities.cap_range.range_2ghz_max = 2732; + + if (AR5K_EEPROM_HDR_11B(ee_header)) + __set_bit(AR5K_MODE_11B, + ah->ah_capabilities.cap_mode); + + if (AR5K_EEPROM_HDR_11G(ee_header) && + ah->ah_version != AR5K_AR5211) + __set_bit(AR5K_MODE_11G, + ah->ah_capabilities.cap_mode); + } + } + + /* GPIO */ + ah->ah_gpio_npins = AR5K_NUM_GPIO; + + /* Set number of supported TX queues */ + if (ah->ah_version == AR5K_AR5210) + ah->ah_capabilities.cap_queues.q_tx_num = + AR5K_NUM_TX_QUEUES_NOQCU; + else + ah->ah_capabilities.cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES; + + return 0; +} + +/* Main function used by the driver part to check caps */ +int ath5k_hw_get_capability(struct ath5k_hw *ah, + enum ath5k_capability_type cap_type, + u32 capability, u32 *result) +{ + ATH5K_TRACE(ah->ah_sc); + + switch (cap_type) { + case AR5K_CAP_NUM_TXQUEUES: + if (result) { + if (ah->ah_version == AR5K_AR5210) + *result = AR5K_NUM_TX_QUEUES_NOQCU; + else + *result = AR5K_NUM_TX_QUEUES; + goto yes; + } + case AR5K_CAP_VEOL: + goto yes; + case AR5K_CAP_COMPRESSION: + if (ah->ah_version == AR5K_AR5212) + goto yes; + else + goto no; + case AR5K_CAP_BURST: + goto yes; + case AR5K_CAP_TPC: + goto yes; + case AR5K_CAP_BSSIDMASK: + if (ah->ah_version == AR5K_AR5212) + goto yes; + else + goto no; + case AR5K_CAP_XR: + if (ah->ah_version == AR5K_AR5212) + goto yes; + else + goto no; + default: + goto no; + } + +no: + return -EINVAL; +yes: + return 0; +} + +/* + * TODO: Following functions should be part of a new function + * set_capability + */ + +int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid, + u16 assoc_id) +{ + ATH5K_TRACE(ah->ah_sc); + + if (ah->ah_version == AR5K_AR5210) { + AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, + AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA); + return 0; + } + + return -EIO; +} + +int ath5k_hw_disable_pspoll(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + + if (ah->ah_version == AR5K_AR5210) { + AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, + AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA); + return 0; + } + + return -EIO; +} diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c new file mode 100644 index 00000000000..4904a07e4b5 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2007-2008 Bruno Randolf <bruno@thinktube.com> + * + * This file is free software: you may copy, redistribute and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2005 Atheros Communications, Inc. + * Copyright (c) 2006 Devicescape Software, Inc. + * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com> + * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include "base.h" +#include "debug.h" + +static unsigned int ath5k_debug; +module_param_named(debug, ath5k_debug, uint, 0); + + +#ifdef CONFIG_ATH5K_DEBUG + +#include <linux/seq_file.h> +#include "reg.h" + +static struct dentry *ath5k_global_debugfs; + +static int ath5k_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + + +/* debugfs: registers */ + +struct reg { + const char *name; + int addr; +}; + +#define REG_STRUCT_INIT(r) { #r, r } + +/* just a few random registers, might want to add more */ +static const struct reg regs[] = { + REG_STRUCT_INIT(AR5K_CR), + REG_STRUCT_INIT(AR5K_RXDP), + REG_STRUCT_INIT(AR5K_CFG), + REG_STRUCT_INIT(AR5K_IER), + REG_STRUCT_INIT(AR5K_BCR), + REG_STRUCT_INIT(AR5K_RTSD0), + REG_STRUCT_INIT(AR5K_RTSD1), + REG_STRUCT_INIT(AR5K_TXCFG), + REG_STRUCT_INIT(AR5K_RXCFG), + REG_STRUCT_INIT(AR5K_RXJLA), + REG_STRUCT_INIT(AR5K_MIBC), + REG_STRUCT_INIT(AR5K_TOPS), + REG_STRUCT_INIT(AR5K_RXNOFRM), + REG_STRUCT_INIT(AR5K_TXNOFRM), + REG_STRUCT_INIT(AR5K_RPGTO), + REG_STRUCT_INIT(AR5K_RFCNT), + REG_STRUCT_INIT(AR5K_MISC), + REG_STRUCT_INIT(AR5K_QCUDCU_CLKGT), + REG_STRUCT_INIT(AR5K_ISR), + REG_STRUCT_INIT(AR5K_PISR), + REG_STRUCT_INIT(AR5K_SISR0), + REG_STRUCT_INIT(AR5K_SISR1), + REG_STRUCT_INIT(AR5K_SISR2), + REG_STRUCT_INIT(AR5K_SISR3), + REG_STRUCT_INIT(AR5K_SISR4), + REG_STRUCT_INIT(AR5K_IMR), + REG_STRUCT_INIT(AR5K_PIMR), + REG_STRUCT_INIT(AR5K_SIMR0), + REG_STRUCT_INIT(AR5K_SIMR1), + REG_STRUCT_INIT(AR5K_SIMR2), + REG_STRUCT_INIT(AR5K_SIMR3), + REG_STRUCT_INIT(AR5K_SIMR4), + REG_STRUCT_INIT(AR5K_DCM_ADDR), + REG_STRUCT_INIT(AR5K_DCCFG), + REG_STRUCT_INIT(AR5K_CCFG), + REG_STRUCT_INIT(AR5K_CPC0), + REG_STRUCT_INIT(AR5K_CPC1), + REG_STRUCT_INIT(AR5K_CPC2), + REG_STRUCT_INIT(AR5K_CPC3), + REG_STRUCT_INIT(AR5K_CPCOVF), + REG_STRUCT_INIT(AR5K_RESET_CTL), + REG_STRUCT_INIT(AR5K_SLEEP_CTL), + REG_STRUCT_INIT(AR5K_INTPEND), + REG_STRUCT_INIT(AR5K_SFR), + REG_STRUCT_INIT(AR5K_PCICFG), + REG_STRUCT_INIT(AR5K_GPIOCR), + REG_STRUCT_INIT(AR5K_GPIODO), + REG_STRUCT_INIT(AR5K_SREV), +}; + +static void *reg_start(struct seq_file *seq, loff_t *pos) +{ + return *pos < ARRAY_SIZE(regs) ? (void *)®s[*pos] : NULL; +} + +static void reg_stop(struct seq_file *seq, void *p) +{ + /* nothing to do */ +} + +static void *reg_next(struct seq_file *seq, void *p, loff_t *pos) +{ + ++*pos; + return *pos < ARRAY_SIZE(regs) ? (void *)®s[*pos] : NULL; +} + +static int reg_show(struct seq_file *seq, void *p) +{ + struct ath5k_softc *sc = seq->private; + struct reg *r = p; + seq_printf(seq, "%-25s0x%08x\n", r->name, + ath5k_hw_reg_read(sc->ah, r->addr)); + return 0; +} + +static const struct seq_operations register_seq_ops = { + .start = reg_start, + .next = reg_next, + .stop = reg_stop, + .show = reg_show +}; + +static int open_file_registers(struct inode *inode, struct file *file) +{ + struct seq_file *s; + int res; + res = seq_open(file, ®ister_seq_ops); + if (res == 0) { + s = file->private_data; + s->private = inode->i_private; + } + return res; +} + +static const struct file_operations fops_registers = { + .open = open_file_registers, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, + .owner = THIS_MODULE, +}; + + +/* debugfs: beacons */ + +static ssize_t read_file_beacon(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath5k_softc *sc = file->private_data; + struct ath5k_hw *ah = sc->ah; + char buf[500]; + unsigned int len = 0; + unsigned int v; + u64 tsf; + + v = ath5k_hw_reg_read(sc->ah, AR5K_BEACON); + len += snprintf(buf+len, sizeof(buf)-len, + "%-24s0x%08x\tintval: %d\tTIM: 0x%x\n", + "AR5K_BEACON", v, v & AR5K_BEACON_PERIOD, + (v & AR5K_BEACON_TIM) >> AR5K_BEACON_TIM_S); + + len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\n", + "AR5K_LAST_TSTP", ath5k_hw_reg_read(sc->ah, AR5K_LAST_TSTP)); + + len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\n\n", + "AR5K_BEACON_CNT", ath5k_hw_reg_read(sc->ah, AR5K_BEACON_CNT)); + + v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER0); + len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n", + "AR5K_TIMER0 (TBTT)", v, v); + + v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER1); + len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n", + "AR5K_TIMER1 (DMA)", v, v >> 3); + + v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER2); + len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n", + "AR5K_TIMER2 (SWBA)", v, v >> 3); + + v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER3); + len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n", + "AR5K_TIMER3 (ATIM)", v, v); + + tsf = ath5k_hw_get_tsf64(sc->ah); + len += snprintf(buf+len, sizeof(buf)-len, + "TSF\t\t0x%016llx\tTU: %08x\n", + (unsigned long long)tsf, TSF_TO_TU(tsf)); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_beacon(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ath5k_softc *sc = file->private_data; + struct ath5k_hw *ah = sc->ah; + char buf[20]; + + if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) + return -EFAULT; + + if (strncmp(buf, "disable", 7) == 0) { + AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); + printk(KERN_INFO "debugfs disable beacons\n"); + } else if (strncmp(buf, "enable", 6) == 0) { + AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); + printk(KERN_INFO "debugfs enable beacons\n"); + } + return count; +} + +static const struct file_operations fops_beacon = { + .read = read_file_beacon, + .write = write_file_beacon, + .open = ath5k_debugfs_open, + .owner = THIS_MODULE, +}; + + +/* debugfs: reset */ + +static ssize_t write_file_reset(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ath5k_softc *sc = file->private_data; + tasklet_schedule(&sc->restq); + return count; +} + +static const struct file_operations fops_reset = { + .write = write_file_reset, + .open = ath5k_debugfs_open, + .owner = THIS_MODULE, +}; + + +/* debugfs: debug level */ + +static const struct { + enum ath5k_debug_level level; + const char *name; + const char *desc; +} dbg_info[] = { + { ATH5K_DEBUG_RESET, "reset", "reset and initialization" }, + { ATH5K_DEBUG_INTR, "intr", "interrupt handling" }, + { ATH5K_DEBUG_MODE, "mode", "mode init/setup" }, + { ATH5K_DEBUG_XMIT, "xmit", "basic xmit operation" }, + { ATH5K_DEBUG_BEACON, "beacon", "beacon handling" }, + { ATH5K_DEBUG_CALIBRATE, "calib", "periodic calibration" }, + { ATH5K_DEBUG_TXPOWER, "txpower", "transmit power setting" }, + { ATH5K_DEBUG_LED, "led", "LED management" }, + { ATH5K_DEBUG_DUMP_RX, "dumprx", "print received skb content" }, + { ATH5K_DEBUG_DUMP_TX, "dumptx", "print transmit skb content" }, + { ATH5K_DEBUG_DUMPBANDS, "dumpbands", "dump bands" }, + { ATH5K_DEBUG_TRACE, "trace", "trace function calls" }, + { ATH5K_DEBUG_ANY, "all", "show all debug levels" }, +}; + +static ssize_t read_file_debug(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath5k_softc *sc = file->private_data; + char buf[700]; + unsigned int len = 0; + unsigned int i; + + len += snprintf(buf+len, sizeof(buf)-len, + "DEBUG LEVEL: 0x%08x\n\n", sc->debug.level); + + for (i = 0; i < ARRAY_SIZE(dbg_info) - 1; i++) { + len += snprintf(buf+len, sizeof(buf)-len, + "%10s %c 0x%08x - %s\n", dbg_info[i].name, + sc->debug.level & dbg_info[i].level ? '+' : ' ', + dbg_info[i].level, dbg_info[i].desc); + } + len += snprintf(buf+len, sizeof(buf)-len, + "%10s %c 0x%08x - %s\n", dbg_info[i].name, + sc->debug.level == dbg_info[i].level ? '+' : ' ', + dbg_info[i].level, dbg_info[i].desc); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_debug(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ath5k_softc *sc = file->private_data; + unsigned int i; + char buf[20]; + + if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(dbg_info); i++) { + if (strncmp(buf, dbg_info[i].name, + strlen(dbg_info[i].name)) == 0) { + sc->debug.level ^= dbg_info[i].level; /* toggle bit */ + break; + } + } + return count; +} + +static const struct file_operations fops_debug = { + .read = read_file_debug, + .write = write_file_debug, + .open = ath5k_debugfs_open, + .owner = THIS_MODULE, +}; + + +/* init */ + +void +ath5k_debug_init(void) +{ + ath5k_global_debugfs = debugfs_create_dir("ath5k", NULL); +} + +void +ath5k_debug_init_device(struct ath5k_softc *sc) +{ + sc->debug.level = ath5k_debug; + + sc->debug.debugfs_phydir = debugfs_create_dir(wiphy_name(sc->hw->wiphy), + ath5k_global_debugfs); + + sc->debug.debugfs_debug = debugfs_create_file("debug", S_IWUSR | S_IRUGO, + sc->debug.debugfs_phydir, sc, &fops_debug); + + sc->debug.debugfs_registers = debugfs_create_file("registers", S_IRUGO, + sc->debug.debugfs_phydir, sc, &fops_registers); + + sc->debug.debugfs_beacon = debugfs_create_file("beacon", S_IWUSR | S_IRUGO, + sc->debug.debugfs_phydir, sc, &fops_beacon); + + sc->debug.debugfs_reset = debugfs_create_file("reset", S_IWUSR, + sc->debug.debugfs_phydir, sc, &fops_reset); +} + +void +ath5k_debug_finish(void) +{ + debugfs_remove(ath5k_global_debugfs); +} + +void +ath5k_debug_finish_device(struct ath5k_softc *sc) +{ + debugfs_remove(sc->debug.debugfs_debug); + debugfs_remove(sc->debug.debugfs_registers); + debugfs_remove(sc->debug.debugfs_beacon); + debugfs_remove(sc->debug.debugfs_reset); + debugfs_remove(sc->debug.debugfs_phydir); +} + + +/* functions used in other places */ + +void +ath5k_debug_dump_bands(struct ath5k_softc *sc) +{ + unsigned int b, i; + + if (likely(!(sc->debug.level & ATH5K_DEBUG_DUMPBANDS))) + return; + + BUG_ON(!sc->sbands); + + for (b = 0; b < IEEE80211_NUM_BANDS; b++) { + struct ieee80211_supported_band *band = &sc->sbands[b]; + char bname[6]; + switch (band->band) { + case IEEE80211_BAND_2GHZ: + strcpy(bname, "2 GHz"); + break; + case IEEE80211_BAND_5GHZ: + strcpy(bname, "5 GHz"); + break; + default: + printk(KERN_DEBUG "Band not supported: %d\n", + band->band); + return; + } + printk(KERN_DEBUG "Band %s: channels %d, rates %d\n", bname, + band->n_channels, band->n_bitrates); + printk(KERN_DEBUG " channels:\n"); + for (i = 0; i < band->n_channels; i++) + printk(KERN_DEBUG " %3d %d %.4x %.4x\n", + ieee80211_frequency_to_channel( + band->channels[i].center_freq), + band->channels[i].center_freq, + band->channels[i].hw_value, + band->channels[i].flags); + printk(KERN_DEBUG " rates:\n"); + for (i = 0; i < band->n_bitrates; i++) + printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n", + band->bitrates[i].bitrate, + band->bitrates[i].hw_value, + band->bitrates[i].flags, + band->bitrates[i].hw_value_short); + } +} + +static inline void +ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done, + struct ath5k_rx_status *rs) +{ + struct ath5k_desc *ds = bf->desc; + struct ath5k_hw_all_rx_desc *rd = &ds->ud.ds_rx; + + printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n", + ds, (unsigned long long)bf->daddr, + ds->ds_link, ds->ds_data, + rd->rx_ctl.rx_control_0, rd->rx_ctl.rx_control_1, + rd->u.rx_stat.rx_status_0, rd->u.rx_stat.rx_status_0, + !done ? ' ' : (rs->rs_status == 0) ? '*' : '!'); +} + +void +ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah) +{ + struct ath5k_desc *ds; + struct ath5k_buf *bf; + struct ath5k_rx_status rs = {}; + int status; + + if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET))) + return; + + printk(KERN_DEBUG "rx queue %x, link %p\n", + ath5k_hw_get_rxdp(ah), sc->rxlink); + + spin_lock_bh(&sc->rxbuflock); + list_for_each_entry(bf, &sc->rxbuf, list) { + ds = bf->desc; + status = ah->ah_proc_rx_desc(ah, ds, &rs); + if (!status) + ath5k_debug_printrxbuf(bf, status == 0, &rs); + } + spin_unlock_bh(&sc->rxbuflock); +} + +void +ath5k_debug_dump_skb(struct ath5k_softc *sc, + struct sk_buff *skb, const char *prefix, int tx) +{ + char buf[16]; + + if (likely(!((tx && (sc->debug.level & ATH5K_DEBUG_DUMP_TX)) || + (!tx && (sc->debug.level & ATH5K_DEBUG_DUMP_RX))))) + return; + + snprintf(buf, sizeof(buf), "%s %s", wiphy_name(sc->hw->wiphy), prefix); + + print_hex_dump_bytes(buf, DUMP_PREFIX_NONE, skb->data, + min(200U, skb->len)); + + printk(KERN_DEBUG "\n"); +} + +void +ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf) +{ + struct ath5k_desc *ds = bf->desc; + struct ath5k_hw_5212_tx_desc *td = &ds->ud.ds_tx5212; + struct ath5k_tx_status ts = {}; + int done; + + if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET))) + return; + + done = sc->ah->ah_proc_tx_desc(sc->ah, bf->desc, &ts); + + printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x " + "%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link, + ds->ds_data, td->tx_ctl.tx_control_0, td->tx_ctl.tx_control_1, + td->tx_ctl.tx_control_2, td->tx_ctl.tx_control_3, + td->tx_stat.tx_status_0, td->tx_stat.tx_status_1, + done ? ' ' : (ts.ts_status == 0) ? '*' : '!'); +} + +#endif /* ifdef CONFIG_ATH5K_DEBUG */ diff --git a/drivers/net/wireless/ath/ath5k/debug.h b/drivers/net/wireless/ath/ath5k/debug.h new file mode 100644 index 00000000000..66f69f04e55 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/debug.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2007 Bruno Randolf <bruno@thinktube.com> + * + * This file is free software: you may copy, redistribute and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2005 Atheros Communications, Inc. + * Copyright (c) 2006 Devicescape Software, Inc. + * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com> + * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef _ATH5K_DEBUG_H +#define _ATH5K_DEBUG_H + +struct ath5k_softc; +struct ath5k_hw; +struct sk_buff; +struct ath5k_buf; + +struct ath5k_dbg_info { + unsigned int level; /* debug level */ + /* debugfs entries */ + struct dentry *debugfs_phydir; + struct dentry *debugfs_debug; + struct dentry *debugfs_registers; + struct dentry *debugfs_beacon; + struct dentry *debugfs_reset; +}; + +/** + * enum ath5k_debug_level - ath5k debug level + * + * @ATH5K_DEBUG_RESET: reset processing + * @ATH5K_DEBUG_INTR: interrupt handling + * @ATH5K_DEBUG_MODE: mode init/setup + * @ATH5K_DEBUG_XMIT: basic xmit operation + * @ATH5K_DEBUG_BEACON: beacon handling + * @ATH5K_DEBUG_CALIBRATE: periodic calibration + * @ATH5K_DEBUG_TXPOWER: transmit power setting + * @ATH5K_DEBUG_LED: led management + * @ATH5K_DEBUG_DUMP_RX: print received skb content + * @ATH5K_DEBUG_DUMP_TX: print transmit skb content + * @ATH5K_DEBUG_DUMPBANDS: dump bands + * @ATH5K_DEBUG_TRACE: trace function calls + * @ATH5K_DEBUG_ANY: show at any debug level + * + * The debug level is used to control the amount and type of debugging output + * we want to see. The debug level is given in calls to ATH5K_DBG to specify + * where the message should appear, and the user can control the debugging + * messages he wants to see, either by the module parameter 'debug' on module + * load, or dynamically by using debugfs 'ath5k/phyX/debug'. these levels can + * be combined together by bitwise OR. + */ +enum ath5k_debug_level { + ATH5K_DEBUG_RESET = 0x00000001, + ATH5K_DEBUG_INTR = 0x00000002, + ATH5K_DEBUG_MODE = 0x00000004, + ATH5K_DEBUG_XMIT = 0x00000008, + ATH5K_DEBUG_BEACON = 0x00000010, + ATH5K_DEBUG_CALIBRATE = 0x00000020, + ATH5K_DEBUG_TXPOWER = 0x00000040, + ATH5K_DEBUG_LED = 0x00000080, + ATH5K_DEBUG_DUMP_RX = 0x00000100, + ATH5K_DEBUG_DUMP_TX = 0x00000200, + ATH5K_DEBUG_DUMPBANDS = 0x00000400, + ATH5K_DEBUG_TRACE = 0x00001000, + ATH5K_DEBUG_ANY = 0xffffffff +}; + +#ifdef CONFIG_ATH5K_DEBUG + +#define ATH5K_TRACE(_sc) do { \ + if (unlikely((_sc)->debug.level & ATH5K_DEBUG_TRACE)) \ + printk(KERN_DEBUG "ath5k trace %s:%d\n", __func__, __LINE__); \ + } while (0) + +#define ATH5K_DBG(_sc, _m, _fmt, ...) do { \ + if (unlikely((_sc)->debug.level & (_m) && net_ratelimit())) \ + ATH5K_PRINTK(_sc, KERN_DEBUG, "(%s:%d): " _fmt, \ + __func__, __LINE__, ##__VA_ARGS__); \ + } while (0) + +#define ATH5K_DBG_UNLIMIT(_sc, _m, _fmt, ...) do { \ + if (unlikely((_sc)->debug.level & (_m))) \ + ATH5K_PRINTK(_sc, KERN_DEBUG, "(%s:%d): " _fmt, \ + __func__, __LINE__, ##__VA_ARGS__); \ + } while (0) + +void +ath5k_debug_init(void); + +void +ath5k_debug_init_device(struct ath5k_softc *sc); + +void +ath5k_debug_finish(void); + +void +ath5k_debug_finish_device(struct ath5k_softc *sc); + +void +ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah); + +void +ath5k_debug_dump_bands(struct ath5k_softc *sc); + +void +ath5k_debug_dump_skb(struct ath5k_softc *sc, + struct sk_buff *skb, const char *prefix, int tx); + +void +ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf); + +#else /* no debugging */ + +#include <linux/compiler.h> + +#define ATH5K_TRACE(_sc) typecheck(struct ath5k_softc *, (_sc)) + +static inline void __attribute__ ((format (printf, 3, 4))) +ATH5K_DBG(struct ath5k_softc *sc, unsigned int m, const char *fmt, ...) {} + +static inline void __attribute__ ((format (printf, 3, 4))) +ATH5K_DBG_UNLIMIT(struct ath5k_softc *sc, unsigned int m, const char *fmt, ...) +{} + +static inline void +ath5k_debug_init(void) {} + +static inline void +ath5k_debug_init_device(struct ath5k_softc *sc) {} + +static inline void +ath5k_debug_finish(void) {} + +static inline void +ath5k_debug_finish_device(struct ath5k_softc *sc) {} + +static inline void +ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah) {} + +static inline void +ath5k_debug_dump_bands(struct ath5k_softc *sc) {} + +static inline void +ath5k_debug_dump_skb(struct ath5k_softc *sc, + struct sk_buff *skb, const char *prefix, int tx) {} + +static inline void +ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf) {} + +#endif /* ifdef CONFIG_ATH5K_DEBUG */ + +#endif /* ifndef _ATH5K_DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath5k/desc.c b/drivers/net/wireless/ath/ath5k/desc.c new file mode 100644 index 00000000000..dc30a2b70a6 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/desc.c @@ -0,0 +1,696 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2007-2008 Pavel Roskin <proski@gnu.org> + * + * Permission to use, copy, modify, and 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. + * + */ + +/******************************\ + Hardware Descriptor Functions +\******************************/ + +#include "ath5k.h" +#include "reg.h" +#include "debug.h" +#include "base.h" + +/* + * TX Descriptors + */ + +/* + * Initialize the 2-word tx control descriptor on 5210/5211 + */ +static int +ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, + unsigned int pkt_len, unsigned int hdr_len, enum ath5k_pkt_type type, + unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0, + unsigned int key_index, unsigned int antenna_mode, unsigned int flags, + unsigned int rtscts_rate, unsigned int rtscts_duration) +{ + u32 frame_type; + struct ath5k_hw_2w_tx_ctl *tx_ctl; + unsigned int frame_len; + + tx_ctl = &desc->ud.ds_tx5210.tx_ctl; + + /* + * Validate input + * - Zero retries don't make sense. + * - A zero rate will put the HW into a mode where it continously sends + * noise on the channel, so it is important to avoid this. + */ + if (unlikely(tx_tries0 == 0)) { + ATH5K_ERR(ah->ah_sc, "zero retries\n"); + WARN_ON(1); + return -EINVAL; + } + if (unlikely(tx_rate0 == 0)) { + ATH5K_ERR(ah->ah_sc, "zero rate\n"); + WARN_ON(1); + return -EINVAL; + } + + /* Clear descriptor */ + memset(&desc->ud.ds_tx5210, 0, sizeof(struct ath5k_hw_5210_tx_desc)); + + /* Setup control descriptor */ + + /* Verify and set frame length */ + + /* remove padding we might have added before */ + frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN; + + if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN) + return -EINVAL; + + tx_ctl->tx_control_0 = frame_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN; + + /* Verify and set buffer length */ + + /* NB: beacon's BufLen must be a multiple of 4 bytes */ + if (type == AR5K_PKT_TYPE_BEACON) + pkt_len = roundup(pkt_len, 4); + + if (pkt_len & ~AR5K_2W_TX_DESC_CTL1_BUF_LEN) + return -EINVAL; + + tx_ctl->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN; + + /* + * Verify and set header length + * XXX: I only found that on 5210 code, does it work on 5211 ? + */ + if (ah->ah_version == AR5K_AR5210) { + if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN) + return -EINVAL; + tx_ctl->tx_control_0 |= + AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN); + } + + /*Diferences between 5210-5211*/ + if (ah->ah_version == AR5K_AR5210) { + switch (type) { + case AR5K_PKT_TYPE_BEACON: + case AR5K_PKT_TYPE_PROBE_RESP: + frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY; + case AR5K_PKT_TYPE_PIFS: + frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS; + default: + frame_type = type /*<< 2 ?*/; + } + + tx_ctl->tx_control_0 |= + AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) | + AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE); + + } else { + tx_ctl->tx_control_0 |= + AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) | + AR5K_REG_SM(antenna_mode, + AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT); + tx_ctl->tx_control_1 |= + AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE); + } +#define _TX_FLAGS(_c, _flag) \ + if (flags & AR5K_TXDESC_##_flag) { \ + tx_ctl->tx_control_##_c |= \ + AR5K_2W_TX_DESC_CTL##_c##_##_flag; \ + } + + _TX_FLAGS(0, CLRDMASK); + _TX_FLAGS(0, VEOL); + _TX_FLAGS(0, INTREQ); + _TX_FLAGS(0, RTSENA); + _TX_FLAGS(1, NOACK); + +#undef _TX_FLAGS + + /* + * WEP crap + */ + if (key_index != AR5K_TXKEYIX_INVALID) { + tx_ctl->tx_control_0 |= + AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID; + tx_ctl->tx_control_1 |= + AR5K_REG_SM(key_index, + AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX); + } + + /* + * RTS/CTS Duration [5210 ?] + */ + if ((ah->ah_version == AR5K_AR5210) && + (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA))) + tx_ctl->tx_control_1 |= rtscts_duration & + AR5K_2W_TX_DESC_CTL1_RTS_DURATION; + + return 0; +} + +/* + * Initialize the 4-word tx control descriptor on 5212 + */ +static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah, + struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len, + enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0, + unsigned int tx_tries0, unsigned int key_index, + unsigned int antenna_mode, unsigned int flags, + unsigned int rtscts_rate, + unsigned int rtscts_duration) +{ + struct ath5k_hw_4w_tx_ctl *tx_ctl; + unsigned int frame_len; + + ATH5K_TRACE(ah->ah_sc); + tx_ctl = &desc->ud.ds_tx5212.tx_ctl; + + /* + * Validate input + * - Zero retries don't make sense. + * - A zero rate will put the HW into a mode where it continously sends + * noise on the channel, so it is important to avoid this. + */ + if (unlikely(tx_tries0 == 0)) { + ATH5K_ERR(ah->ah_sc, "zero retries\n"); + WARN_ON(1); + return -EINVAL; + } + if (unlikely(tx_rate0 == 0)) { + ATH5K_ERR(ah->ah_sc, "zero rate\n"); + WARN_ON(1); + return -EINVAL; + } + + tx_power += ah->ah_txpower.txp_offset; + if (tx_power > AR5K_TUNE_MAX_TXPOWER) + tx_power = AR5K_TUNE_MAX_TXPOWER; + + /* Clear descriptor */ + memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc)); + + /* Setup control descriptor */ + + /* Verify and set frame length */ + + /* remove padding we might have added before */ + frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN; + + if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN) + return -EINVAL; + + tx_ctl->tx_control_0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN; + + /* Verify and set buffer length */ + + /* NB: beacon's BufLen must be a multiple of 4 bytes */ + if (type == AR5K_PKT_TYPE_BEACON) + pkt_len = roundup(pkt_len, 4); + + if (pkt_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN) + return -EINVAL; + + tx_ctl->tx_control_1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN; + + tx_ctl->tx_control_0 |= + AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) | + AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT); + tx_ctl->tx_control_1 |= AR5K_REG_SM(type, + AR5K_4W_TX_DESC_CTL1_FRAME_TYPE); + tx_ctl->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES, + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0); + tx_ctl->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0; + +#define _TX_FLAGS(_c, _flag) \ + if (flags & AR5K_TXDESC_##_flag) { \ + tx_ctl->tx_control_##_c |= \ + AR5K_4W_TX_DESC_CTL##_c##_##_flag; \ + } + + _TX_FLAGS(0, CLRDMASK); + _TX_FLAGS(0, VEOL); + _TX_FLAGS(0, INTREQ); + _TX_FLAGS(0, RTSENA); + _TX_FLAGS(0, CTSENA); + _TX_FLAGS(1, NOACK); + +#undef _TX_FLAGS + + /* + * WEP crap + */ + if (key_index != AR5K_TXKEYIX_INVALID) { + tx_ctl->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID; + tx_ctl->tx_control_1 |= AR5K_REG_SM(key_index, + AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX); + } + + /* + * RTS/CTS + */ + if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) { + if ((flags & AR5K_TXDESC_RTSENA) && + (flags & AR5K_TXDESC_CTSENA)) + return -EINVAL; + tx_ctl->tx_control_2 |= rtscts_duration & + AR5K_4W_TX_DESC_CTL2_RTS_DURATION; + tx_ctl->tx_control_3 |= AR5K_REG_SM(rtscts_rate, + AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE); + } + + return 0; +} + +/* + * Initialize a 4-word multi rate retry tx control descriptor on 5212 + */ +static int +ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, + unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, + u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3) +{ + struct ath5k_hw_4w_tx_ctl *tx_ctl; + + /* + * Rates can be 0 as long as the retry count is 0 too. + * A zero rate and nonzero retry count will put the HW into a mode where + * it continously sends noise on the channel, so it is important to + * avoid this. + */ + if (unlikely((tx_rate1 == 0 && tx_tries1 != 0) || + (tx_rate2 == 0 && tx_tries2 != 0) || + (tx_rate3 == 0 && tx_tries3 != 0))) { + ATH5K_ERR(ah->ah_sc, "zero rate\n"); + WARN_ON(1); + return -EINVAL; + } + + if (ah->ah_version == AR5K_AR5212) { + tx_ctl = &desc->ud.ds_tx5212.tx_ctl; + +#define _XTX_TRIES(_n) \ + if (tx_tries##_n) { \ + tx_ctl->tx_control_2 |= \ + AR5K_REG_SM(tx_tries##_n, \ + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n); \ + tx_ctl->tx_control_3 |= \ + AR5K_REG_SM(tx_rate##_n, \ + AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n); \ + } + + _XTX_TRIES(1); + _XTX_TRIES(2); + _XTX_TRIES(3); + +#undef _XTX_TRIES + + return 1; + } + + return 0; +} + +/* no mrr support for cards older than 5212 */ +static int +ath5k_hw_setup_no_mrr(struct ath5k_hw *ah, struct ath5k_desc *desc, + unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, + u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3) +{ + return 0; +} + +/* + * Proccess the tx status descriptor on 5210/5211 + */ +static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah, + struct ath5k_desc *desc, struct ath5k_tx_status *ts) +{ + struct ath5k_hw_2w_tx_ctl *tx_ctl; + struct ath5k_hw_tx_status *tx_status; + + ATH5K_TRACE(ah->ah_sc); + + tx_ctl = &desc->ud.ds_tx5210.tx_ctl; + tx_status = &desc->ud.ds_tx5210.tx_stat; + + /* No frame has been send or error */ + if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0)) + return -EINPROGRESS; + + /* + * Get descriptor status + */ + ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP); + ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT); + ts->ts_longretry = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT); + /*TODO: ts->ts_virtcol + test*/ + ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1, + AR5K_DESC_TX_STATUS1_SEQ_NUM); + ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1, + AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH); + ts->ts_antenna = 1; + ts->ts_status = 0; + ts->ts_rate[0] = AR5K_REG_MS(tx_ctl->tx_control_0, + AR5K_2W_TX_DESC_CTL0_XMIT_RATE); + ts->ts_retry[0] = ts->ts_longretry; + ts->ts_final_idx = 0; + + if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) { + if (tx_status->tx_status_0 & + AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES) + ts->ts_status |= AR5K_TXERR_XRETRY; + + if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN) + ts->ts_status |= AR5K_TXERR_FIFO; + + if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED) + ts->ts_status |= AR5K_TXERR_FILT; + } + + return 0; +} + +/* + * Proccess a tx status descriptor on 5212 + */ +static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah, + struct ath5k_desc *desc, struct ath5k_tx_status *ts) +{ + struct ath5k_hw_4w_tx_ctl *tx_ctl; + struct ath5k_hw_tx_status *tx_status; + + ATH5K_TRACE(ah->ah_sc); + + tx_ctl = &desc->ud.ds_tx5212.tx_ctl; + tx_status = &desc->ud.ds_tx5212.tx_stat; + + /* No frame has been send or error */ + if (unlikely(!(tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE))) + return -EINPROGRESS; + + /* + * Get descriptor status + */ + ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP); + ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT); + ts->ts_longretry = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT); + ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1, + AR5K_DESC_TX_STATUS1_SEQ_NUM); + ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1, + AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH); + ts->ts_antenna = (tx_status->tx_status_1 & + AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1; + ts->ts_status = 0; + + ts->ts_final_idx = AR5K_REG_MS(tx_status->tx_status_1, + AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX); + + /* The longretry counter has the number of un-acked retries + * for the final rate. To get the total number of retries + * we have to add the retry counters for the other rates + * as well + */ + ts->ts_retry[ts->ts_final_idx] = ts->ts_longretry; + switch (ts->ts_final_idx) { + case 3: + ts->ts_rate[3] = AR5K_REG_MS(tx_ctl->tx_control_3, + AR5K_4W_TX_DESC_CTL3_XMIT_RATE3); + + ts->ts_retry[2] = AR5K_REG_MS(tx_ctl->tx_control_2, + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2); + ts->ts_longretry += ts->ts_retry[2]; + /* fall through */ + case 2: + ts->ts_rate[2] = AR5K_REG_MS(tx_ctl->tx_control_3, + AR5K_4W_TX_DESC_CTL3_XMIT_RATE2); + + ts->ts_retry[1] = AR5K_REG_MS(tx_ctl->tx_control_2, + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1); + ts->ts_longretry += ts->ts_retry[1]; + /* fall through */ + case 1: + ts->ts_rate[1] = AR5K_REG_MS(tx_ctl->tx_control_3, + AR5K_4W_TX_DESC_CTL3_XMIT_RATE1); + + ts->ts_retry[0] = AR5K_REG_MS(tx_ctl->tx_control_2, + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1); + ts->ts_longretry += ts->ts_retry[0]; + /* fall through */ + case 0: + ts->ts_rate[0] = tx_ctl->tx_control_3 & + AR5K_4W_TX_DESC_CTL3_XMIT_RATE0; + break; + } + + /* TX error */ + if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) { + if (tx_status->tx_status_0 & + AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES) + ts->ts_status |= AR5K_TXERR_XRETRY; + + if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN) + ts->ts_status |= AR5K_TXERR_FIFO; + + if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED) + ts->ts_status |= AR5K_TXERR_FILT; + } + + return 0; +} + +/* + * RX Descriptors + */ + +/* + * Initialize an rx control descriptor + */ +static int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, + u32 size, unsigned int flags) +{ + struct ath5k_hw_rx_ctl *rx_ctl; + + ATH5K_TRACE(ah->ah_sc); + rx_ctl = &desc->ud.ds_rx.rx_ctl; + + /* + * Clear the descriptor + * If we don't clean the status descriptor, + * while scanning we get too many results, + * most of them virtual, after some secs + * of scanning system hangs. M.F. + */ + memset(&desc->ud.ds_rx, 0, sizeof(struct ath5k_hw_all_rx_desc)); + + /* Setup descriptor */ + rx_ctl->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN; + if (unlikely(rx_ctl->rx_control_1 != size)) + return -EINVAL; + + if (flags & AR5K_RXDESC_INTREQ) + rx_ctl->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ; + + return 0; +} + +/* + * Proccess the rx status descriptor on 5210/5211 + */ +static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah, + struct ath5k_desc *desc, struct ath5k_rx_status *rs) +{ + struct ath5k_hw_rx_status *rx_status; + + rx_status = &desc->ud.ds_rx.u.rx_stat; + + /* No frame received / not ready */ + if (unlikely(!(rx_status->rx_status_1 & + AR5K_5210_RX_DESC_STATUS1_DONE))) + return -EINPROGRESS; + + /* + * Frame receive status + */ + rs->rs_datalen = rx_status->rx_status_0 & + AR5K_5210_RX_DESC_STATUS0_DATA_LEN; + rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL); + rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE); + rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA); + rs->rs_more = !!(rx_status->rx_status_0 & + AR5K_5210_RX_DESC_STATUS0_MORE); + /* TODO: this timestamp is 13 bit, later on we assume 15 bit */ + rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1, + AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP); + rs->rs_status = 0; + rs->rs_phyerr = 0; + + /* + * Key table status + */ + if (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID) + rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1, + AR5K_5210_RX_DESC_STATUS1_KEY_INDEX); + else + rs->rs_keyix = AR5K_RXKEYIX_INVALID; + + /* + * Receive/descriptor errors + */ + if (!(rx_status->rx_status_1 & + AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) { + if (rx_status->rx_status_1 & + AR5K_5210_RX_DESC_STATUS1_CRC_ERROR) + rs->rs_status |= AR5K_RXERR_CRC; + + if (rx_status->rx_status_1 & + AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN) + rs->rs_status |= AR5K_RXERR_FIFO; + + if (rx_status->rx_status_1 & + AR5K_5210_RX_DESC_STATUS1_PHY_ERROR) { + rs->rs_status |= AR5K_RXERR_PHY; + rs->rs_phyerr |= AR5K_REG_MS(rx_status->rx_status_1, + AR5K_5210_RX_DESC_STATUS1_PHY_ERROR); + } + + if (rx_status->rx_status_1 & + AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR) + rs->rs_status |= AR5K_RXERR_DECRYPT; + } + + return 0; +} + +/* + * Proccess the rx status descriptor on 5212 + */ +static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah, + struct ath5k_desc *desc, struct ath5k_rx_status *rs) +{ + struct ath5k_hw_rx_status *rx_status; + struct ath5k_hw_rx_error *rx_err; + + ATH5K_TRACE(ah->ah_sc); + rx_status = &desc->ud.ds_rx.u.rx_stat; + + /* Overlay on error */ + rx_err = &desc->ud.ds_rx.u.rx_err; + + /* No frame received / not ready */ + if (unlikely(!(rx_status->rx_status_1 & + AR5K_5212_RX_DESC_STATUS1_DONE))) + return -EINPROGRESS; + + /* + * Frame receive status + */ + rs->rs_datalen = rx_status->rx_status_0 & + AR5K_5212_RX_DESC_STATUS0_DATA_LEN; + rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL); + rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE); + rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA); + rs->rs_more = !!(rx_status->rx_status_0 & + AR5K_5212_RX_DESC_STATUS0_MORE); + rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1, + AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP); + rs->rs_status = 0; + rs->rs_phyerr = 0; + + /* + * Key table status + */ + if (rx_status->rx_status_1 & AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID) + rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1, + AR5K_5212_RX_DESC_STATUS1_KEY_INDEX); + else + rs->rs_keyix = AR5K_RXKEYIX_INVALID; + + /* + * Receive/descriptor errors + */ + if (!(rx_status->rx_status_1 & + AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) { + if (rx_status->rx_status_1 & + AR5K_5212_RX_DESC_STATUS1_CRC_ERROR) + rs->rs_status |= AR5K_RXERR_CRC; + + if (rx_status->rx_status_1 & + AR5K_5212_RX_DESC_STATUS1_PHY_ERROR) { + rs->rs_status |= AR5K_RXERR_PHY; + rs->rs_phyerr |= AR5K_REG_MS(rx_err->rx_error_1, + AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE); + } + + if (rx_status->rx_status_1 & + AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR) + rs->rs_status |= AR5K_RXERR_DECRYPT; + + if (rx_status->rx_status_1 & + AR5K_5212_RX_DESC_STATUS1_MIC_ERROR) + rs->rs_status |= AR5K_RXERR_MIC; + } + + return 0; +} + +/* + * Init function pointers inside ath5k_hw struct + */ +int ath5k_hw_init_desc_functions(struct ath5k_hw *ah) +{ + + if (ah->ah_version != AR5K_AR5210 && + ah->ah_version != AR5K_AR5211 && + ah->ah_version != AR5K_AR5212) + return -ENOTSUPP; + + /* XXX: What is this magic value and where is it used ? */ + if (ah->ah_version == AR5K_AR5212) + ah->ah_magic = AR5K_EEPROM_MAGIC_5212; + else if (ah->ah_version == AR5K_AR5211) + ah->ah_magic = AR5K_EEPROM_MAGIC_5211; + + if (ah->ah_version == AR5K_AR5212) { + ah->ah_setup_rx_desc = ath5k_hw_setup_rx_desc; + ah->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc; + ah->ah_setup_mrr_tx_desc = ath5k_hw_setup_mrr_tx_desc; + ah->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status; + } else { + ah->ah_setup_rx_desc = ath5k_hw_setup_rx_desc; + ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc; + ah->ah_setup_mrr_tx_desc = ath5k_hw_setup_no_mrr; + ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status; + } + + if (ah->ah_version == AR5K_AR5212) + ah->ah_proc_rx_desc = ath5k_hw_proc_5212_rx_status; + else if (ah->ah_version <= AR5K_AR5211) + ah->ah_proc_rx_desc = ath5k_hw_proc_5210_rx_status; + + return 0; +} + diff --git a/drivers/net/wireless/ath/ath5k/desc.h b/drivers/net/wireless/ath/ath5k/desc.h new file mode 100644 index 00000000000..56158c804e3 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/desc.h @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +/* + * Internal RX/TX descriptor structures + * (rX: reserved fields possibily used by future versions of the ar5k chipset) + */ + +/* + * common hardware RX control descriptor + */ +struct ath5k_hw_rx_ctl { + u32 rx_control_0; /* RX control word 0 */ + u32 rx_control_1; /* RX control word 1 */ +} __packed; + +/* RX control word 0 field/sflags */ +#define AR5K_DESC_RX_CTL0 0x00000000 + +/* RX control word 1 fields/flags */ +#define AR5K_DESC_RX_CTL1_BUF_LEN 0x00000fff +#define AR5K_DESC_RX_CTL1_INTREQ 0x00002000 + +/* + * common hardware RX status descriptor + * 5210/11 and 5212 differ only in the flags defined below + */ +struct ath5k_hw_rx_status { + u32 rx_status_0; /* RX status word 0 */ + u32 rx_status_1; /* RX status word 1 */ +} __packed; + +/* 5210/5211 */ +/* RX status word 0 fields/flags */ +#define AR5K_5210_RX_DESC_STATUS0_DATA_LEN 0x00000fff +#define AR5K_5210_RX_DESC_STATUS0_MORE 0x00001000 +#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE 0x00078000 +#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE_S 15 +#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x07f80000 +#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 19 +#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA 0x38000000 +#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 27 + +/* RX status word 1 fields/flags */ +#define AR5K_5210_RX_DESC_STATUS1_DONE 0x00000001 +#define AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 +#define AR5K_5210_RX_DESC_STATUS1_CRC_ERROR 0x00000004 +#define AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN 0x00000008 +#define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010 +#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR 0x000000e0 +#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR_S 5 +#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 +#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX 0x00007e00 +#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_S 9 +#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x0fff8000 +#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 15 +#define AR5K_5210_RX_DESC_STATUS1_KEY_CACHE_MISS 0x10000000 + +/* 5212 */ +/* RX status word 0 fields/flags */ +#define AR5K_5212_RX_DESC_STATUS0_DATA_LEN 0x00000fff +#define AR5K_5212_RX_DESC_STATUS0_MORE 0x00001000 +#define AR5K_5212_RX_DESC_STATUS0_DECOMP_CRC_ERROR 0x00002000 +#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE 0x000f8000 +#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE_S 15 +#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x0ff00000 +#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 20 +#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA 0xf0000000 +#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 28 + +/* RX status word 1 fields/flags */ +#define AR5K_5212_RX_DESC_STATUS1_DONE 0x00000001 +#define AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 +#define AR5K_5212_RX_DESC_STATUS1_CRC_ERROR 0x00000004 +#define AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000008 +#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR 0x00000010 +#define AR5K_5212_RX_DESC_STATUS1_MIC_ERROR 0x00000020 +#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 +#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX 0x0000fe00 +#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_S 9 +#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x7fff0000 +#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 16 +#define AR5K_5212_RX_DESC_STATUS1_KEY_CACHE_MISS 0x80000000 + +/* + * common hardware RX error descriptor + */ +struct ath5k_hw_rx_error { + u32 rx_error_0; /* RX status word 0 */ + u32 rx_error_1; /* RX status word 1 */ +} __packed; + +/* RX error word 0 fields/flags */ +#define AR5K_RX_DESC_ERROR0 0x00000000 + +/* RX error word 1 fields/flags */ +#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE 0x0000ff00 +#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE_S 8 + +/* PHY Error codes */ +#define AR5K_DESC_RX_PHY_ERROR_NONE 0x00 +#define AR5K_DESC_RX_PHY_ERROR_TIMING 0x20 +#define AR5K_DESC_RX_PHY_ERROR_PARITY 0x40 +#define AR5K_DESC_RX_PHY_ERROR_RATE 0x60 +#define AR5K_DESC_RX_PHY_ERROR_LENGTH 0x80 +#define AR5K_DESC_RX_PHY_ERROR_64QAM 0xa0 +#define AR5K_DESC_RX_PHY_ERROR_SERVICE 0xc0 +#define AR5K_DESC_RX_PHY_ERROR_TRANSMITOVR 0xe0 + +/* + * 5210/5211 hardware 2-word TX control descriptor + */ +struct ath5k_hw_2w_tx_ctl { + u32 tx_control_0; /* TX control word 0 */ + u32 tx_control_1; /* TX control word 1 */ +} __packed; + +/* TX control word 0 fields/flags */ +#define AR5K_2W_TX_DESC_CTL0_FRAME_LEN 0x00000fff +#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN 0x0003f000 /*[5210 ?]*/ +#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_S 12 +#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE 0x003c0000 +#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE_S 18 +#define AR5K_2W_TX_DESC_CTL0_RTSENA 0x00400000 +#define AR5K_2W_TX_DESC_CTL0_CLRDMASK 0x01000000 +#define AR5K_2W_TX_DESC_CTL0_LONG_PACKET 0x00800000 /*[5210]*/ +#define AR5K_2W_TX_DESC_CTL0_VEOL 0x00800000 /*[5211]*/ +#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE 0x1c000000 /*[5210]*/ +#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_S 26 +#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 0x02000000 +#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211 0x1e000000 + +#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT \ + (ah->ah_version == AR5K_AR5210 ? \ + AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 : \ + AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211) + +#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25 +#define AR5K_2W_TX_DESC_CTL0_INTREQ 0x20000000 +#define AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000 + +/* TX control word 1 fields/flags */ +#define AR5K_2W_TX_DESC_CTL1_BUF_LEN 0x00000fff +#define AR5K_2W_TX_DESC_CTL1_MORE 0x00001000 +#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210 0x0007e000 +#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211 0x000fe000 + +#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX \ + (ah->ah_version == AR5K_AR5210 ? \ + AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210 : \ + AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211) + +#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S 13 +#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE 0x00700000 /*[5211]*/ +#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_S 20 +#define AR5K_2W_TX_DESC_CTL1_NOACK 0x00800000 /*[5211]*/ +#define AR5K_2W_TX_DESC_CTL1_RTS_DURATION 0xfff80000 /*[5210 ?]*/ + +/* Frame types */ +#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NORMAL 0x00 +#define AR5K_AR5210_TX_DESC_FRAME_TYPE_ATIM 0x04 +#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PSPOLL 0x08 +#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY 0x0c +#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS 0x10 + +/* + * 5212 hardware 4-word TX control descriptor + */ +struct ath5k_hw_4w_tx_ctl { + u32 tx_control_0; /* TX control word 0 */ + +#define AR5K_4W_TX_DESC_CTL0_FRAME_LEN 0x00000fff +#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER 0x003f0000 +#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER_S 16 +#define AR5K_4W_TX_DESC_CTL0_RTSENA 0x00400000 +#define AR5K_4W_TX_DESC_CTL0_VEOL 0x00800000 +#define AR5K_4W_TX_DESC_CTL0_CLRDMASK 0x01000000 +#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT 0x1e000000 +#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25 +#define AR5K_4W_TX_DESC_CTL0_INTREQ 0x20000000 +#define AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000 +#define AR5K_4W_TX_DESC_CTL0_CTSENA 0x80000000 + + u32 tx_control_1; /* TX control word 1 */ + +#define AR5K_4W_TX_DESC_CTL1_BUF_LEN 0x00000fff +#define AR5K_4W_TX_DESC_CTL1_MORE 0x00001000 +#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX 0x000fe000 +#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S 13 +#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE 0x00f00000 +#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE_S 20 +#define AR5K_4W_TX_DESC_CTL1_NOACK 0x01000000 +#define AR5K_4W_TX_DESC_CTL1_COMP_PROC 0x06000000 +#define AR5K_4W_TX_DESC_CTL1_COMP_PROC_S 25 +#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN 0x18000000 +#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN_S 27 +#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN 0x60000000 +#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN_S 29 + + u32 tx_control_2; /* TX control word 2 */ + +#define AR5K_4W_TX_DESC_CTL2_RTS_DURATION 0x00007fff +#define AR5K_4W_TX_DESC_CTL2_DURATION_UPDATE_ENABLE 0x00008000 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0 0x000f0000 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0_S 16 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1 0x00f00000 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1_S 20 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2 0x0f000000 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2_S 24 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3 0xf0000000 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3_S 28 + + u32 tx_control_3; /* TX control word 3 */ + +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE0 0x0000001f +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1 0x000003e0 +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1_S 5 +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2 0x00007c00 +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2_S 10 +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3 0x000f8000 +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3_S 15 +#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE 0x01f00000 +#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE_S 20 +} __packed; + +/* + * Common TX status descriptor + */ +struct ath5k_hw_tx_status { + u32 tx_status_0; /* TX status word 0 */ + u32 tx_status_1; /* TX status word 1 */ +} __packed; + +/* TX status word 0 fields/flags */ +#define AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK 0x00000001 +#define AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES 0x00000002 +#define AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN 0x00000004 +#define AR5K_DESC_TX_STATUS0_FILTERED 0x00000008 +/*??? +#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT 0x000000f0 +#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT_S 4 +*/ +#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT 0x000000f0 +#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT_S 4 +/*??? +#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT 0x00000f00 +#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT_S 8 +*/ +#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT 0x00000f00 +#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT_S 8 +#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT 0x0000f000 +#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT_S 12 +#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP 0xffff0000 +#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP_S 16 + +/* TX status word 1 fields/flags */ +#define AR5K_DESC_TX_STATUS1_DONE 0x00000001 +#define AR5K_DESC_TX_STATUS1_SEQ_NUM 0x00001ffe +#define AR5K_DESC_TX_STATUS1_SEQ_NUM_S 1 +#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH 0x001fe000 +#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH_S 13 +#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX 0x00600000 +#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX_S 21 +#define AR5K_DESC_TX_STATUS1_COMP_SUCCESS 0x00800000 +#define AR5K_DESC_TX_STATUS1_XMIT_ANTENNA 0x01000000 + +/* + * 5210/5211 hardware TX descriptor + */ +struct ath5k_hw_5210_tx_desc { + struct ath5k_hw_2w_tx_ctl tx_ctl; + struct ath5k_hw_tx_status tx_stat; +} __packed; + +/* + * 5212 hardware TX descriptor + */ +struct ath5k_hw_5212_tx_desc { + struct ath5k_hw_4w_tx_ctl tx_ctl; + struct ath5k_hw_tx_status tx_stat; +} __packed; + +/* + * common hardware RX descriptor + */ +struct ath5k_hw_all_rx_desc { + struct ath5k_hw_rx_ctl rx_ctl; + union { + struct ath5k_hw_rx_status rx_stat; + struct ath5k_hw_rx_error rx_err; + } u; +} __packed; + +/* + * Atheros hardware descriptor + * This is read and written to by the hardware + */ +struct ath5k_desc { + u32 ds_link; /* physical address of the next descriptor */ + u32 ds_data; /* physical address of data buffer (skb) */ + + union { + struct ath5k_hw_5210_tx_desc ds_tx5210; + struct ath5k_hw_5212_tx_desc ds_tx5212; + struct ath5k_hw_all_rx_desc ds_rx; + } ud; +} __packed; + +#define AR5K_RXDESC_INTREQ 0x0020 + +#define AR5K_TXDESC_CLRDMASK 0x0001 +#define AR5K_TXDESC_NOACK 0x0002 /*[5211+]*/ +#define AR5K_TXDESC_RTSENA 0x0004 +#define AR5K_TXDESC_CTSENA 0x0008 +#define AR5K_TXDESC_INTREQ 0x0010 +#define AR5K_TXDESC_VEOL 0x0020 /*[5211+]*/ + diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c new file mode 100644 index 00000000000..941b51130a6 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -0,0 +1,703 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +/*************************************\ +* DMA and interrupt masking functions * +\*************************************/ + +/* + * dma.c - DMA and interrupt masking functions + * + * Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and + * handle queue setup for 5210 chipset (rest are handled on qcu.c). + * Also we setup interrupt mask register (IMR) and read the various iterrupt + * status registers (ISR). + * + * TODO: Handle SISR on 5211+ and introduce a function to return the queue + * number that resulted the interrupt. + */ + +#include "ath5k.h" +#include "reg.h" +#include "debug.h" +#include "base.h" + +/*********\ +* Receive * +\*********/ + +/** + * ath5k_hw_start_rx_dma - Start DMA receive + * + * @ah: The &struct ath5k_hw + */ +void ath5k_hw_start_rx_dma(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR); + ath5k_hw_reg_read(ah, AR5K_CR); +} + +/** + * ath5k_hw_stop_rx_dma - Stop DMA receive + * + * @ah: The &struct ath5k_hw + */ +int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah) +{ + unsigned int i; + + ATH5K_TRACE(ah->ah_sc); + ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR); + + /* + * It may take some time to disable the DMA receive unit + */ + for (i = 1000; i > 0 && + (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0; + i--) + udelay(10); + + return i ? 0 : -EBUSY; +} + +/** + * ath5k_hw_get_rxdp - Get RX Descriptor's address + * + * @ah: The &struct ath5k_hw + */ +u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah) +{ + return ath5k_hw_reg_read(ah, AR5K_RXDP); +} + +/** + * ath5k_hw_set_rxdp - Set RX Descriptor's address + * + * @ah: The &struct ath5k_hw + * @phys_addr: RX descriptor address + * + * XXX: Should we check if rx is enabled before setting rxdp ? + */ +void ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr) +{ + ATH5K_TRACE(ah->ah_sc); + + ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP); +} + + +/**********\ +* Transmit * +\**********/ + +/** + * ath5k_hw_start_tx_dma - Start DMA transmit for a specific queue + * + * @ah: The &struct ath5k_hw + * @queue: The hw queue number + * + * Start DMA transmit for a specific queue and since 5210 doesn't have + * QCU/DCU, set up queue parameters for 5210 here based on queue type (one + * queue for normal data and one queue for beacons). For queue setup + * on newer chips check out qcu.c. Returns -EINVAL if queue number is out + * of range or if queue is already disabled. + * + * NOTE: Must be called after setting up tx control descriptor for that + * queue (see below). + */ +int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue) +{ + u32 tx_queue; + + ATH5K_TRACE(ah->ah_sc); + AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + + /* Return if queue is declared inactive */ + if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) + return -EIO; + + if (ah->ah_version == AR5K_AR5210) { + tx_queue = ath5k_hw_reg_read(ah, AR5K_CR); + + /* + * Set the queue by type on 5210 + */ + switch (ah->ah_txq[queue].tqi_type) { + case AR5K_TX_QUEUE_DATA: + tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0; + break; + case AR5K_TX_QUEUE_BEACON: + tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; + ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, + AR5K_BSR); + break; + case AR5K_TX_QUEUE_CAB: + tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; + ath5k_hw_reg_write(ah, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V | + AR5K_BCR_BDMAE, AR5K_BSR); + break; + default: + return -EINVAL; + } + /* Start queue */ + ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); + ath5k_hw_reg_read(ah, AR5K_CR); + } else { + /* Return if queue is disabled */ + if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue)) + return -EIO; + + /* Start queue */ + AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXE, queue); + } + + return 0; +} + +/** + * ath5k_hw_stop_tx_dma - Stop DMA transmit on a specific queue + * + * @ah: The &struct ath5k_hw + * @queue: The hw queue number + * + * Stop DMA transmit on a specific hw queue and drain queue so we don't + * have any pending frames. Returns -EBUSY if we still have pending frames, + * -EINVAL if queue number is out of range. + * + */ +int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) +{ + unsigned int i = 40; + u32 tx_queue, pending; + + ATH5K_TRACE(ah->ah_sc); + AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + + /* Return if queue is declared inactive */ + if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) + return -EIO; + + if (ah->ah_version == AR5K_AR5210) { + tx_queue = ath5k_hw_reg_read(ah, AR5K_CR); + + /* + * Set by queue type + */ + switch (ah->ah_txq[queue].tqi_type) { + case AR5K_TX_QUEUE_DATA: + tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0; + break; + case AR5K_TX_QUEUE_BEACON: + case AR5K_TX_QUEUE_CAB: + /* XXX Fix me... */ + tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1; + ath5k_hw_reg_write(ah, 0, AR5K_BSR); + break; + default: + return -EINVAL; + } + + /* Stop queue */ + ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); + ath5k_hw_reg_read(ah, AR5K_CR); + } else { + /* + * Schedule TX disable and wait until queue is empty + */ + AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue); + + /*Check for pending frames*/ + do { + pending = ath5k_hw_reg_read(ah, + AR5K_QUEUE_STATUS(queue)) & + AR5K_QCU_STS_FRMPENDCNT; + udelay(100); + } while (--i && pending); + + /* For 2413+ order PCU to drop packets using + * QUIET mechanism */ + if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) && + pending){ + /* Set periodicity and duration */ + ath5k_hw_reg_write(ah, + AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)| + AR5K_REG_SM(10, AR5K_QUIET_CTL2_QT_DUR), + AR5K_QUIET_CTL2); + + /* Enable quiet period for current TSF */ + ath5k_hw_reg_write(ah, + AR5K_QUIET_CTL1_QT_EN | + AR5K_REG_SM(ath5k_hw_reg_read(ah, + AR5K_TSF_L32_5211) >> 10, + AR5K_QUIET_CTL1_NEXT_QT_TSF), + AR5K_QUIET_CTL1); + + /* Force channel idle high */ + AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211, + AR5K_DIAG_SW_CHANEL_IDLE_HIGH); + + /* Wait a while and disable mechanism */ + udelay(200); + AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1, + AR5K_QUIET_CTL1_QT_EN); + + /* Re-check for pending frames */ + i = 40; + do { + pending = ath5k_hw_reg_read(ah, + AR5K_QUEUE_STATUS(queue)) & + AR5K_QCU_STS_FRMPENDCNT; + udelay(100); + } while (--i && pending); + + AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211, + AR5K_DIAG_SW_CHANEL_IDLE_HIGH); + } + + /* Clear register */ + ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD); + if (pending) + return -EBUSY; + } + + /* TODO: Check for success on 5210 else return error */ + return 0; +} + +/** + * ath5k_hw_get_txdp - Get TX Descriptor's address for a specific queue + * + * @ah: The &struct ath5k_hw + * @queue: The hw queue number + * + * Get TX descriptor's address for a specific queue. For 5210 we ignore + * the queue number and use tx queue type since we only have 2 queues. + * We use TXDP0 for normal data queue and TXDP1 for beacon queue. + * For newer chips with QCU/DCU we just read the corresponding TXDP register. + * + * XXX: Is TXDP read and clear ? + */ +u32 ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue) +{ + u16 tx_reg; + + ATH5K_TRACE(ah->ah_sc); + AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + + /* + * Get the transmit queue descriptor pointer from the selected queue + */ + /*5210 doesn't have QCU*/ + if (ah->ah_version == AR5K_AR5210) { + switch (ah->ah_txq[queue].tqi_type) { + case AR5K_TX_QUEUE_DATA: + tx_reg = AR5K_NOQCU_TXDP0; + break; + case AR5K_TX_QUEUE_BEACON: + case AR5K_TX_QUEUE_CAB: + tx_reg = AR5K_NOQCU_TXDP1; + break; + default: + return 0xffffffff; + } + } else { + tx_reg = AR5K_QUEUE_TXDP(queue); + } + + return ath5k_hw_reg_read(ah, tx_reg); +} + +/** + * ath5k_hw_set_txdp - Set TX Descriptor's address for a specific queue + * + * @ah: The &struct ath5k_hw + * @queue: The hw queue number + * + * Set TX descriptor's address for a specific queue. For 5210 we ignore + * the queue number and we use tx queue type since we only have 2 queues + * so as above we use TXDP0 for normal data queue and TXDP1 for beacon queue. + * For newer chips with QCU/DCU we just set the corresponding TXDP register. + * Returns -EINVAL if queue type is invalid for 5210 and -EIO if queue is still + * active. + */ +int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr) +{ + u16 tx_reg; + + ATH5K_TRACE(ah->ah_sc); + AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + + /* + * Set the transmit queue descriptor pointer register by type + * on 5210 + */ + if (ah->ah_version == AR5K_AR5210) { + switch (ah->ah_txq[queue].tqi_type) { + case AR5K_TX_QUEUE_DATA: + tx_reg = AR5K_NOQCU_TXDP0; + break; + case AR5K_TX_QUEUE_BEACON: + case AR5K_TX_QUEUE_CAB: + tx_reg = AR5K_NOQCU_TXDP1; + break; + default: + return -EINVAL; + } + } else { + /* + * Set the transmit queue descriptor pointer for + * the selected queue on QCU for 5211+ + * (this won't work if the queue is still active) + */ + if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) + return -EIO; + + tx_reg = AR5K_QUEUE_TXDP(queue); + } + + /* Set descriptor pointer */ + ath5k_hw_reg_write(ah, phys_addr, tx_reg); + + return 0; +} + +/** + * ath5k_hw_update_tx_triglevel - Update tx trigger level + * + * @ah: The &struct ath5k_hw + * @increase: Flag to force increase of trigger level + * + * This function increases/decreases the tx trigger level for the tx fifo + * buffer (aka FIFO threshold) that is used to indicate when PCU flushes + * the buffer and transmits it's data. Lowering this results sending small + * frames more quickly but can lead to tx underruns, raising it a lot can + * result other problems (i think bmiss is related). Right now we start with + * the lowest possible (64Bytes) and if we get tx underrun we increase it using + * the increase flag. Returns -EIO if we have have reached maximum/minimum. + * + * XXX: Link this with tx DMA size ? + * XXX: Use it to save interrupts ? + * TODO: Needs testing, i think it's related to bmiss... + */ +int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase) +{ + u32 trigger_level, imr; + int ret = -EIO; + + ATH5K_TRACE(ah->ah_sc); + + /* + * Disable interrupts by setting the mask + */ + imr = ath5k_hw_set_imr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL); + + trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG), + AR5K_TXCFG_TXFULL); + + if (!increase) { + if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES) + goto done; + } else + trigger_level += + ((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2); + + /* + * Update trigger level on success + */ + if (ah->ah_version == AR5K_AR5210) + ath5k_hw_reg_write(ah, trigger_level, AR5K_TRIG_LVL); + else + AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, + AR5K_TXCFG_TXFULL, trigger_level); + + ret = 0; + +done: + /* + * Restore interrupt mask + */ + ath5k_hw_set_imr(ah, imr); + + return ret; +} + +/*******************\ +* Interrupt masking * +\*******************/ + +/** + * ath5k_hw_is_intr_pending - Check if we have pending interrupts + * + * @ah: The &struct ath5k_hw + * + * Check if we have pending interrupts to process. Returns 1 if we + * have pending interrupts and 0 if we haven't. + */ +bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0; +} + +/** + * ath5k_hw_get_isr - Get interrupt status + * + * @ah: The @struct ath5k_hw + * @interrupt_mask: Driver's interrupt mask used to filter out + * interrupts in sw. + * + * This function is used inside our interrupt handler to determine the reason + * for the interrupt by reading Primary Interrupt Status Register. Returns an + * abstract interrupt status mask which is mostly ISR with some uncommon bits + * being mapped on some standard non hw-specific positions + * (check out &ath5k_int). + * + * NOTE: We use read-and-clear register, so after this function is called ISR + * is zeroed. + */ +int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) +{ + u32 data; + + ATH5K_TRACE(ah->ah_sc); + + /* + * Read interrupt status from the Interrupt Status register + * on 5210 + */ + if (ah->ah_version == AR5K_AR5210) { + data = ath5k_hw_reg_read(ah, AR5K_ISR); + if (unlikely(data == AR5K_INT_NOCARD)) { + *interrupt_mask = data; + return -ENODEV; + } + } else { + /* + * Read interrupt status from Interrupt + * Status Register shadow copy (Read And Clear) + * + * Note: PISR/SISR Not available on 5210 + */ + data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR); + if (unlikely(data == AR5K_INT_NOCARD)) { + *interrupt_mask = data; + return -ENODEV; + } + } + + /* + * Get abstract interrupt mask (driver-compatible) + */ + *interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr; + + if (ah->ah_version != AR5K_AR5210) { + u32 sisr2 = ath5k_hw_reg_read(ah, AR5K_RAC_SISR2); + + /*HIU = Host Interface Unit (PCI etc)*/ + if (unlikely(data & (AR5K_ISR_HIUERR))) + *interrupt_mask |= AR5K_INT_FATAL; + + /*Beacon Not Ready*/ + if (unlikely(data & (AR5K_ISR_BNR))) + *interrupt_mask |= AR5K_INT_BNR; + + if (unlikely(sisr2 & (AR5K_SISR2_SSERR | + AR5K_SISR2_DPERR | + AR5K_SISR2_MCABT))) + *interrupt_mask |= AR5K_INT_FATAL; + + if (data & AR5K_ISR_TIM) + *interrupt_mask |= AR5K_INT_TIM; + + if (data & AR5K_ISR_BCNMISC) { + if (sisr2 & AR5K_SISR2_TIM) + *interrupt_mask |= AR5K_INT_TIM; + if (sisr2 & AR5K_SISR2_DTIM) + *interrupt_mask |= AR5K_INT_DTIM; + if (sisr2 & AR5K_SISR2_DTIM_SYNC) + *interrupt_mask |= AR5K_INT_DTIM_SYNC; + if (sisr2 & AR5K_SISR2_BCN_TIMEOUT) + *interrupt_mask |= AR5K_INT_BCN_TIMEOUT; + if (sisr2 & AR5K_SISR2_CAB_TIMEOUT) + *interrupt_mask |= AR5K_INT_CAB_TIMEOUT; + } + + if (data & AR5K_ISR_RXDOPPLER) + *interrupt_mask |= AR5K_INT_RX_DOPPLER; + if (data & AR5K_ISR_QCBRORN) { + *interrupt_mask |= AR5K_INT_QCBRORN; + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR3), + AR5K_SISR3_QCBRORN); + } + if (data & AR5K_ISR_QCBRURN) { + *interrupt_mask |= AR5K_INT_QCBRURN; + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR3), + AR5K_SISR3_QCBRURN); + } + if (data & AR5K_ISR_QTRIG) { + *interrupt_mask |= AR5K_INT_QTRIG; + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR4), + AR5K_SISR4_QTRIG); + } + + if (data & AR5K_ISR_TXOK) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR0), + AR5K_SISR0_QCU_TXOK); + + if (data & AR5K_ISR_TXDESC) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR0), + AR5K_SISR0_QCU_TXDESC); + + if (data & AR5K_ISR_TXERR) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR1), + AR5K_SISR1_QCU_TXERR); + + if (data & AR5K_ISR_TXEOL) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR1), + AR5K_SISR1_QCU_TXEOL); + + if (data & AR5K_ISR_TXURN) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR2), + AR5K_SISR2_QCU_TXURN); + } else { + if (unlikely(data & (AR5K_ISR_SSERR | AR5K_ISR_MCABT + | AR5K_ISR_HIUERR | AR5K_ISR_DPERR))) + *interrupt_mask |= AR5K_INT_FATAL; + + /* + * XXX: BMISS interrupts may occur after association. + * I found this on 5210 code but it needs testing. If this is + * true we should disable them before assoc and re-enable them + * after a successful assoc + some jiffies. + interrupt_mask &= ~AR5K_INT_BMISS; + */ + } + + /* + * In case we didn't handle anything, + * print the register value. + */ + if (unlikely(*interrupt_mask == 0 && net_ratelimit())) + ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr); + + return 0; +} + +/** + * ath5k_hw_set_imr - Set interrupt mask + * + * @ah: The &struct ath5k_hw + * @new_mask: The new interrupt mask to be set + * + * Set the interrupt mask in hw to save interrupts. We do that by mapping + * ath5k_int bits to hw-specific bits to remove abstraction and writing + * Interrupt Mask Register. + */ +enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) +{ + enum ath5k_int old_mask, int_mask; + + old_mask = ah->ah_imr; + + /* + * Disable card interrupts to prevent any race conditions + * (they will be re-enabled afterwards if AR5K_INT GLOBAL + * is set again on the new mask). + */ + if (old_mask & AR5K_INT_GLOBAL) { + ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); + ath5k_hw_reg_read(ah, AR5K_IER); + } + + /* + * Add additional, chipset-dependent interrupt mask flags + * and write them to the IMR (interrupt mask register). + */ + int_mask = new_mask & AR5K_INT_COMMON; + + if (ah->ah_version != AR5K_AR5210) { + /* Preserve per queue TXURN interrupt mask */ + u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2) + & AR5K_SIMR2_QCU_TXURN; + + if (new_mask & AR5K_INT_FATAL) { + int_mask |= AR5K_IMR_HIUERR; + simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR + | AR5K_SIMR2_DPERR); + } + + /*Beacon Not Ready*/ + if (new_mask & AR5K_INT_BNR) + int_mask |= AR5K_INT_BNR; + + if (new_mask & AR5K_INT_TIM) + int_mask |= AR5K_IMR_TIM; + + if (new_mask & AR5K_INT_TIM) + simr2 |= AR5K_SISR2_TIM; + if (new_mask & AR5K_INT_DTIM) + simr2 |= AR5K_SISR2_DTIM; + if (new_mask & AR5K_INT_DTIM_SYNC) + simr2 |= AR5K_SISR2_DTIM_SYNC; + if (new_mask & AR5K_INT_BCN_TIMEOUT) + simr2 |= AR5K_SISR2_BCN_TIMEOUT; + if (new_mask & AR5K_INT_CAB_TIMEOUT) + simr2 |= AR5K_SISR2_CAB_TIMEOUT; + + if (new_mask & AR5K_INT_RX_DOPPLER) + int_mask |= AR5K_IMR_RXDOPPLER; + + /* Note: Per queue interrupt masks + * are set via reset_tx_queue (qcu.c) */ + ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR); + ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2); + + } else { + if (new_mask & AR5K_INT_FATAL) + int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT + | AR5K_IMR_HIUERR | AR5K_IMR_DPERR); + + ath5k_hw_reg_write(ah, int_mask, AR5K_IMR); + } + + /* If RXNOFRM interrupt is masked disable it + * by setting AR5K_RXNOFRM to zero */ + if (!(new_mask & AR5K_INT_RXNOFRM)) + ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM); + + /* Store new interrupt mask */ + ah->ah_imr = new_mask; + + /* ..re-enable interrupts if AR5K_INT_GLOBAL is set */ + if (new_mask & AR5K_INT_GLOBAL) { + ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER); + ath5k_hw_reg_read(ah, AR5K_IER); + } + + return old_mask; +} + diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c new file mode 100644 index 00000000000..c56b494d417 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -0,0 +1,1802 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org> + * + * Permission to use, copy, modify, and 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. + * + */ + +/*************************************\ +* EEPROM access functions and helpers * +\*************************************/ + +#include "ath5k.h" +#include "reg.h" +#include "debug.h" +#include "base.h" + +/* + * Read from eeprom + */ +static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data) +{ + u32 status, timeout; + + ATH5K_TRACE(ah->ah_sc); + /* + * Initialize EEPROM access + */ + if (ah->ah_version == AR5K_AR5210) { + AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_EEAE); + (void)ath5k_hw_reg_read(ah, AR5K_EEPROM_BASE + (4 * offset)); + } else { + ath5k_hw_reg_write(ah, offset, AR5K_EEPROM_BASE); + AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD, + AR5K_EEPROM_CMD_READ); + } + + for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) { + status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS); + if (status & AR5K_EEPROM_STAT_RDDONE) { + if (status & AR5K_EEPROM_STAT_RDERR) + return -EIO; + *data = (u16)(ath5k_hw_reg_read(ah, AR5K_EEPROM_DATA) & + 0xffff); + return 0; + } + udelay(15); + } + + return -ETIMEDOUT; +} + +/* + * Translate binary channel representation in EEPROM to frequency + */ +static u16 ath5k_eeprom_bin2freq(struct ath5k_eeprom_info *ee, u16 bin, + unsigned int mode) +{ + u16 val; + + if (bin == AR5K_EEPROM_CHANNEL_DIS) + return bin; + + if (mode == AR5K_EEPROM_MODE_11A) { + if (ee->ee_version > AR5K_EEPROM_VERSION_3_2) + val = (5 * bin) + 4800; + else + val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 : + (bin * 10) + 5100; + } else { + if (ee->ee_version > AR5K_EEPROM_VERSION_3_2) + val = bin + 2300; + else + val = bin + 2400; + } + + return val; +} + +/* + * Initialize eeprom & capabilities structs + */ +static int +ath5k_eeprom_init_header(struct ath5k_hw *ah) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + int ret; + u16 val; + + /* + * Read values from EEPROM and store them in the capability structure + */ + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header); + + /* Return if we have an old EEPROM */ + if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0) + return 0; + +#ifdef notyet + /* + * Validate the checksum of the EEPROM date. There are some + * devices with invalid EEPROMs. + */ + for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) { + AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val); + cksum ^= val; + } + if (cksum != AR5K_EEPROM_INFO_CKSUM) { + ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum); + return -EIO; + } +#endif + + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version), + ee_ant_gain); + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1); + + /* XXX: Don't know which versions include these two */ + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC2, ee_misc2); + + if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC3, ee_misc3); + + if (ee->ee_version >= AR5K_EEPROM_VERSION_5_0) { + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC4, ee_misc4); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC5, ee_misc5); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC6, ee_misc6); + } + } + + if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) { + AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val); + ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7; + ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7; + + AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val); + ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7; + ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7; + } + + AR5K_EEPROM_READ(AR5K_EEPROM_IS_HB63, val); + + if ((ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4)) && val) + ee->ee_is_hb63 = true; + else + ee->ee_is_hb63 = false; + + AR5K_EEPROM_READ(AR5K_EEPROM_RFKILL, val); + ee->ee_rfkill_pin = (u8) AR5K_REG_MS(val, AR5K_EEPROM_RFKILL_GPIO_SEL); + ee->ee_rfkill_pol = val & AR5K_EEPROM_RFKILL_POLARITY ? true : false; + + return 0; +} + + +/* + * Read antenna infos from eeprom + */ +static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset, + unsigned int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u32 o = *offset; + u16 val; + int ret, i = 0; + + AR5K_EEPROM_READ(o++, val); + ee->ee_switch_settling[mode] = (val >> 8) & 0x7f; + ee->ee_atn_tx_rx[mode] = (val >> 2) & 0x3f; + ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; + ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; + ee->ee_ant_control[mode][i++] = val & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_ant_control[mode][i++] = (val >> 10) & 0x3f; + ee->ee_ant_control[mode][i++] = (val >> 4) & 0x3f; + ee->ee_ant_control[mode][i] = (val << 2) & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_ant_control[mode][i++] |= (val >> 14) & 0x3; + ee->ee_ant_control[mode][i++] = (val >> 8) & 0x3f; + ee->ee_ant_control[mode][i++] = (val >> 2) & 0x3f; + ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; + ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; + ee->ee_ant_control[mode][i++] = val & 0x3f; + + /* Get antenna switch tables */ + ah->ah_ant_ctl[mode][AR5K_ANT_CTL] = + (ee->ee_ant_control[mode][0] << 4); + ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_A] = + ee->ee_ant_control[mode][1] | + (ee->ee_ant_control[mode][2] << 6) | + (ee->ee_ant_control[mode][3] << 12) | + (ee->ee_ant_control[mode][4] << 18) | + (ee->ee_ant_control[mode][5] << 24); + ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_B] = + ee->ee_ant_control[mode][6] | + (ee->ee_ant_control[mode][7] << 6) | + (ee->ee_ant_control[mode][8] << 12) | + (ee->ee_ant_control[mode][9] << 18) | + (ee->ee_ant_control[mode][10] << 24); + + /* return new offset */ + *offset = o; + + return 0; +} + +/* + * Read supported modes and some mode-specific calibration data + * from eeprom + */ +static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, + unsigned int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u32 o = *offset; + u16 val; + int ret; + + ee->ee_n_piers[mode] = 0; + AR5K_EEPROM_READ(o++, val); + ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); + switch(mode) { + case AR5K_EEPROM_MODE_11A: + ee->ee_ob[mode][3] = (val >> 5) & 0x7; + ee->ee_db[mode][3] = (val >> 2) & 0x7; + ee->ee_ob[mode][2] = (val << 1) & 0x7; + + AR5K_EEPROM_READ(o++, val); + ee->ee_ob[mode][2] |= (val >> 15) & 0x1; + ee->ee_db[mode][2] = (val >> 12) & 0x7; + ee->ee_ob[mode][1] = (val >> 9) & 0x7; + ee->ee_db[mode][1] = (val >> 6) & 0x7; + ee->ee_ob[mode][0] = (val >> 3) & 0x7; + ee->ee_db[mode][0] = val & 0x7; + break; + case AR5K_EEPROM_MODE_11G: + case AR5K_EEPROM_MODE_11B: + ee->ee_ob[mode][1] = (val >> 4) & 0x7; + ee->ee_db[mode][1] = val & 0x7; + break; + } + + AR5K_EEPROM_READ(o++, val); + ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff; + ee->ee_thr_62[mode] = val & 0xff; + + if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) + ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28; + + AR5K_EEPROM_READ(o++, val); + ee->ee_tx_end2xpa_disable[mode] = (val >> 8) & 0xff; + ee->ee_tx_frm2xpa_enable[mode] = val & 0xff; + + AR5K_EEPROM_READ(o++, val); + ee->ee_pga_desired_size[mode] = (val >> 8) & 0xff; + + if ((val & 0xff) & 0x80) + ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1); + else + ee->ee_noise_floor_thr[mode] = val & 0xff; + + if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) + ee->ee_noise_floor_thr[mode] = + mode == AR5K_EEPROM_MODE_11A ? -54 : -1; + + AR5K_EEPROM_READ(o++, val); + ee->ee_xlna_gain[mode] = (val >> 5) & 0xff; + ee->ee_x_gain[mode] = (val >> 1) & 0xf; + ee->ee_xpd[mode] = val & 0x1; + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) + ee->ee_fixed_bias[mode] = (val >> 13) & 0x1; + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) { + AR5K_EEPROM_READ(o++, val); + ee->ee_false_detect[mode] = (val >> 6) & 0x7f; + + if (mode == AR5K_EEPROM_MODE_11A) + ee->ee_xr_power[mode] = val & 0x3f; + else { + ee->ee_ob[mode][0] = val & 0x7; + ee->ee_db[mode][0] = (val >> 3) & 0x7; + } + } + + if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_4) { + ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN; + ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA; + } else { + ee->ee_i_gain[mode] = (val >> 13) & 0x7; + + AR5K_EEPROM_READ(o++, val); + ee->ee_i_gain[mode] |= (val << 3) & 0x38; + + if (mode == AR5K_EEPROM_MODE_11G) { + ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff; + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6) + ee->ee_scaled_cck_delta = (val >> 11) & 0x1f; + } + } + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 && + mode == AR5K_EEPROM_MODE_11A) { + ee->ee_i_cal[mode] = (val >> 8) & 0x3f; + ee->ee_q_cal[mode] = (val >> 3) & 0x1f; + } + + if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_0) + goto done; + + /* Note: >= v5 have bg freq piers on another location + * so these freq piers are ignored for >= v5 (should be 0xff + * anyway) */ + switch(mode) { + case AR5K_EEPROM_MODE_11A: + if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_1) + break; + + AR5K_EEPROM_READ(o++, val); + ee->ee_margin_tx_rx[mode] = val & 0x3f; + break; + case AR5K_EEPROM_MODE_11B: + AR5K_EEPROM_READ(o++, val); + + ee->ee_pwr_cal_b[0].freq = + ath5k_eeprom_bin2freq(ee, val & 0xff, mode); + if (ee->ee_pwr_cal_b[0].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + ee->ee_pwr_cal_b[1].freq = + ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); + if (ee->ee_pwr_cal_b[1].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + AR5K_EEPROM_READ(o++, val); + ee->ee_pwr_cal_b[2].freq = + ath5k_eeprom_bin2freq(ee, val & 0xff, mode); + if (ee->ee_pwr_cal_b[2].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) + ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + break; + case AR5K_EEPROM_MODE_11G: + AR5K_EEPROM_READ(o++, val); + + ee->ee_pwr_cal_g[0].freq = + ath5k_eeprom_bin2freq(ee, val & 0xff, mode); + if (ee->ee_pwr_cal_g[0].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + ee->ee_pwr_cal_g[1].freq = + ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); + if (ee->ee_pwr_cal_g[1].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + AR5K_EEPROM_READ(o++, val); + ee->ee_turbo_max_power[mode] = val & 0x7f; + ee->ee_xr_power[mode] = (val >> 7) & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_pwr_cal_g[2].freq = + ath5k_eeprom_bin2freq(ee, val & 0xff, mode); + if (ee->ee_pwr_cal_g[2].freq != AR5K_EEPROM_CHANNEL_DIS) + ee->ee_n_piers[mode]++; + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) + ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_i_cal[mode] = (val >> 8) & 0x3f; + ee->ee_q_cal[mode] = (val >> 3) & 0x1f; + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) { + AR5K_EEPROM_READ(o++, val); + ee->ee_cck_ofdm_gain_delta = val & 0xff; + } + break; + } + +done: + /* return new offset */ + *offset = o; + + return 0; +} + +/* + * Read turbo mode information on newer EEPROM versions + */ +static int +ath5k_eeprom_read_turbo_modes(struct ath5k_hw *ah, + u32 *offset, unsigned int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u32 o = *offset; + u16 val; + int ret; + + if (ee->ee_version < AR5K_EEPROM_VERSION_5_0) + return 0; + + switch (mode){ + case AR5K_EEPROM_MODE_11A: + ee->ee_switch_settling_turbo[mode] = (val >> 6) & 0x7f; + + ee->ee_atn_tx_rx_turbo[mode] = (val >> 13) & 0x7; + AR5K_EEPROM_READ(o++, val); + ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x7) << 3; + ee->ee_margin_tx_rx_turbo[mode] = (val >> 3) & 0x3f; + + ee->ee_adc_desired_size_turbo[mode] = (val >> 9) & 0x7f; + AR5K_EEPROM_READ(o++, val); + ee->ee_adc_desired_size_turbo[mode] |= (val & 0x1) << 7; + ee->ee_pga_desired_size_turbo[mode] = (val >> 1) & 0xff; + + if (AR5K_EEPROM_EEMAP(ee->ee_misc0) >=2) + ee->ee_pd_gain_overlap = (val >> 9) & 0xf; + break; + case AR5K_EEPROM_MODE_11G: + ee->ee_switch_settling_turbo[mode] = (val >> 8) & 0x7f; + + ee->ee_atn_tx_rx_turbo[mode] = (val >> 15) & 0x7; + AR5K_EEPROM_READ(o++, val); + ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x1f) << 1; + ee->ee_margin_tx_rx_turbo[mode] = (val >> 5) & 0x3f; + + ee->ee_adc_desired_size_turbo[mode] = (val >> 11) & 0x7f; + AR5K_EEPROM_READ(o++, val); + ee->ee_adc_desired_size_turbo[mode] |= (val & 0x7) << 5; + ee->ee_pga_desired_size_turbo[mode] = (val >> 3) & 0xff; + break; + } + + /* return new offset */ + *offset = o; + + return 0; +} + +/* Read mode-specific data (except power calibration data) */ +static int +ath5k_eeprom_init_modes(struct ath5k_hw *ah) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u32 mode_offset[3]; + unsigned int mode; + u32 offset; + int ret; + + /* + * Get values for all modes + */ + mode_offset[AR5K_EEPROM_MODE_11A] = AR5K_EEPROM_MODES_11A(ah->ah_ee_version); + mode_offset[AR5K_EEPROM_MODE_11B] = AR5K_EEPROM_MODES_11B(ah->ah_ee_version); + mode_offset[AR5K_EEPROM_MODE_11G] = AR5K_EEPROM_MODES_11G(ah->ah_ee_version); + + ee->ee_turbo_max_power[AR5K_EEPROM_MODE_11A] = + AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header); + + for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) { + offset = mode_offset[mode]; + + ret = ath5k_eeprom_read_ants(ah, &offset, mode); + if (ret) + return ret; + + ret = ath5k_eeprom_read_modes(ah, &offset, mode); + if (ret) + return ret; + + ret = ath5k_eeprom_read_turbo_modes(ah, &offset, mode); + if (ret) + return ret; + } + + /* override for older eeprom versions for better performance */ + if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) { + ee->ee_thr_62[AR5K_EEPROM_MODE_11A] = 15; + ee->ee_thr_62[AR5K_EEPROM_MODE_11B] = 28; + ee->ee_thr_62[AR5K_EEPROM_MODE_11G] = 28; + } + + return 0; +} + +/* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff + * frequency mask) */ +static inline int +ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max, + struct ath5k_chan_pcal_info *pc, unsigned int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + int o = *offset; + int i = 0; + u8 freq1, freq2; + int ret; + u16 val; + + ee->ee_n_piers[mode] = 0; + while(i < max) { + AR5K_EEPROM_READ(o++, val); + + freq1 = val & 0xff; + if (!freq1) + break; + + pc[i++].freq = ath5k_eeprom_bin2freq(ee, + freq1, mode); + ee->ee_n_piers[mode]++; + + freq2 = (val >> 8) & 0xff; + if (!freq2) + break; + + pc[i++].freq = ath5k_eeprom_bin2freq(ee, + freq2, mode); + ee->ee_n_piers[mode]++; + } + + /* return new offset */ + *offset = o; + + return 0; +} + +/* Read frequency piers for 802.11a */ +static int +ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info *pcal = ee->ee_pwr_cal_a; + int i, ret; + u16 val; + u8 mask; + + if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) { + ath5k_eeprom_read_freq_list(ah, &offset, + AR5K_EEPROM_N_5GHZ_CHAN, pcal, + AR5K_EEPROM_MODE_11A); + } else { + mask = AR5K_EEPROM_FREQ_M(ah->ah_ee_version); + + AR5K_EEPROM_READ(offset++, val); + pcal[0].freq = (val >> 9) & mask; + pcal[1].freq = (val >> 2) & mask; + pcal[2].freq = (val << 5) & mask; + + AR5K_EEPROM_READ(offset++, val); + pcal[2].freq |= (val >> 11) & 0x1f; + pcal[3].freq = (val >> 4) & mask; + pcal[4].freq = (val << 3) & mask; + + AR5K_EEPROM_READ(offset++, val); + pcal[4].freq |= (val >> 13) & 0x7; + pcal[5].freq = (val >> 6) & mask; + pcal[6].freq = (val << 1) & mask; + + AR5K_EEPROM_READ(offset++, val); + pcal[6].freq |= (val >> 15) & 0x1; + pcal[7].freq = (val >> 8) & mask; + pcal[8].freq = (val >> 1) & mask; + pcal[9].freq = (val << 6) & mask; + + AR5K_EEPROM_READ(offset++, val); + pcal[9].freq |= (val >> 10) & 0x3f; + + /* Fixed number of piers */ + ee->ee_n_piers[AR5K_EEPROM_MODE_11A] = 10; + + for (i = 0; i < AR5K_EEPROM_N_5GHZ_CHAN; i++) { + pcal[i].freq = ath5k_eeprom_bin2freq(ee, + pcal[i].freq, AR5K_EEPROM_MODE_11A); + } + } + + return 0; +} + +/* Read frequency piers for 802.11bg on eeprom versions >= 5 and eemap >= 2 */ +static inline int +ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info *pcal; + + switch(mode) { + case AR5K_EEPROM_MODE_11B: + pcal = ee->ee_pwr_cal_b; + break; + case AR5K_EEPROM_MODE_11G: + pcal = ee->ee_pwr_cal_g; + break; + default: + return -EINVAL; + } + + ath5k_eeprom_read_freq_list(ah, &offset, + AR5K_EEPROM_N_2GHZ_CHAN_2413, pcal, + mode); + + return 0; +} + +/* + * Read power calibration for RF5111 chips + * + * For RF5111 we have an XPD -eXternal Power Detector- curve + * for each calibrated channel. Each curve has 0,5dB Power steps + * on x axis and PCDAC steps (offsets) on y axis and looks like an + * exponential function. To recreate the curve we read 11 points + * here and interpolate later. + */ + +/* Used to match PCDAC steps with power values on RF5111 chips + * (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC + * steps that match with the power values we read from eeprom. On + * older eeprom versions (< 3.2) these steps are equaly spaced at + * 10% of the pcdac curve -until the curve reaches it's maximum- + * (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2) + * these 11 steps are spaced in a different way. This function returns + * the pcdac steps based on eeprom version and curve min/max so that we + * can have pcdac/pwr points. + */ +static inline void +ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp) +{ + static const u16 intercepts3[] = + { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 }; + static const u16 intercepts3_2[] = + { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; + const u16 *ip; + int i; + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2) + ip = intercepts3_2; + else + ip = intercepts3; + + for (i = 0; i < ARRAY_SIZE(intercepts3); i++) + vp[i] = (ip[i] * max + (100 - ip[i]) * min) / 100; +} + +/* Convert RF5111 specific data to generic raw data + * used by interpolation code */ +static int +ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode, + struct ath5k_chan_pcal_info *chinfo) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info_rf5111 *pcinfo; + struct ath5k_pdgain_info *pd; + u8 pier, point, idx; + u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; + + /* Fill raw data for each calibration pier */ + for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { + + pcinfo = &chinfo[pier].rf5111_info; + + /* Allocate pd_curves for this cal pier */ + chinfo[pier].pd_curves = + kcalloc(AR5K_EEPROM_N_PD_CURVES, + sizeof(struct ath5k_pdgain_info), + GFP_KERNEL); + + if (!chinfo[pier].pd_curves) + return -ENOMEM; + + /* Only one curve for RF5111 + * find out which one and place + * in in pd_curves. + * Note: ee_x_gain is reversed here */ + for (idx = 0; idx < AR5K_EEPROM_N_PD_CURVES; idx++) { + + if (!((ee->ee_x_gain[mode] >> idx) & 0x1)) { + pdgain_idx[0] = idx; + break; + } + } + + ee->ee_pd_gains[mode] = 1; + + pd = &chinfo[pier].pd_curves[idx]; + + pd->pd_points = AR5K_EEPROM_N_PWR_POINTS_5111; + + /* Allocate pd points for this curve */ + pd->pd_step = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111, + sizeof(u8), GFP_KERNEL); + if (!pd->pd_step) + return -ENOMEM; + + pd->pd_pwr = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111, + sizeof(s16), GFP_KERNEL); + if (!pd->pd_pwr) + return -ENOMEM; + + /* Fill raw dataset + * (convert power to 0.25dB units + * for RF5112 combatibility) */ + for (point = 0; point < pd->pd_points; point++) { + + /* Absolute values */ + pd->pd_pwr[point] = 2 * pcinfo->pwr[point]; + + /* Already sorted */ + pd->pd_step[point] = pcinfo->pcdac[point]; + } + + /* Set min/max pwr */ + chinfo[pier].min_pwr = pd->pd_pwr[0]; + chinfo[pier].max_pwr = pd->pd_pwr[10]; + + } + + return 0; +} + +/* Parse EEPROM data */ +static int +ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info *pcal; + int offset, ret; + int i; + u16 val; + + offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); + switch(mode) { + case AR5K_EEPROM_MODE_11A: + if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) + return 0; + + ret = ath5k_eeprom_init_11a_pcal_freq(ah, + offset + AR5K_EEPROM_GROUP1_OFFSET); + if (ret < 0) + return ret; + + offset += AR5K_EEPROM_GROUP2_OFFSET; + pcal = ee->ee_pwr_cal_a; + break; + case AR5K_EEPROM_MODE_11B: + if (!AR5K_EEPROM_HDR_11B(ee->ee_header) && + !AR5K_EEPROM_HDR_11G(ee->ee_header)) + return 0; + + pcal = ee->ee_pwr_cal_b; + offset += AR5K_EEPROM_GROUP3_OFFSET; + + /* fixed piers */ + pcal[0].freq = 2412; + pcal[1].freq = 2447; + pcal[2].freq = 2484; + ee->ee_n_piers[mode] = 3; + break; + case AR5K_EEPROM_MODE_11G: + if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) + return 0; + + pcal = ee->ee_pwr_cal_g; + offset += AR5K_EEPROM_GROUP4_OFFSET; + + /* fixed piers */ + pcal[0].freq = 2312; + pcal[1].freq = 2412; + pcal[2].freq = 2484; + ee->ee_n_piers[mode] = 3; + break; + default: + return -EINVAL; + } + + for (i = 0; i < ee->ee_n_piers[mode]; i++) { + struct ath5k_chan_pcal_info_rf5111 *cdata = + &pcal[i].rf5111_info; + + AR5K_EEPROM_READ(offset++, val); + cdata->pcdac_max = ((val >> 10) & AR5K_EEPROM_PCDAC_M); + cdata->pcdac_min = ((val >> 4) & AR5K_EEPROM_PCDAC_M); + cdata->pwr[0] = ((val << 2) & AR5K_EEPROM_POWER_M); + + AR5K_EEPROM_READ(offset++, val); + cdata->pwr[0] |= ((val >> 14) & 0x3); + cdata->pwr[1] = ((val >> 8) & AR5K_EEPROM_POWER_M); + cdata->pwr[2] = ((val >> 2) & AR5K_EEPROM_POWER_M); + cdata->pwr[3] = ((val << 4) & AR5K_EEPROM_POWER_M); + + AR5K_EEPROM_READ(offset++, val); + cdata->pwr[3] |= ((val >> 12) & 0xf); + cdata->pwr[4] = ((val >> 6) & AR5K_EEPROM_POWER_M); + cdata->pwr[5] = (val & AR5K_EEPROM_POWER_M); + + AR5K_EEPROM_READ(offset++, val); + cdata->pwr[6] = ((val >> 10) & AR5K_EEPROM_POWER_M); + cdata->pwr[7] = ((val >> 4) & AR5K_EEPROM_POWER_M); + cdata->pwr[8] = ((val << 2) & AR5K_EEPROM_POWER_M); + + AR5K_EEPROM_READ(offset++, val); + cdata->pwr[8] |= ((val >> 14) & 0x3); + cdata->pwr[9] = ((val >> 8) & AR5K_EEPROM_POWER_M); + cdata->pwr[10] = ((val >> 2) & AR5K_EEPROM_POWER_M); + + ath5k_get_pcdac_intercepts(ah, cdata->pcdac_min, + cdata->pcdac_max, cdata->pcdac); + } + + return ath5k_eeprom_convert_pcal_info_5111(ah, mode, pcal); +} + + +/* + * Read power calibration for RF5112 chips + * + * For RF5112 we have 4 XPD -eXternal Power Detector- curves + * for each calibrated channel on 0, -6, -12 and -18dbm but we only + * use the higher (3) and the lower (0) curves. Each curve has 0.5dB + * power steps on x axis and PCDAC steps on y axis and looks like a + * linear function. To recreate the curve and pass the power values + * on hw, we read 4 points for xpd 0 (lower gain -> max power) + * and 3 points for xpd 3 (higher gain -> lower power) here and + * interpolate later. + * + * Note: Many vendors just use xpd 0 so xpd 3 is zeroed. + */ + +/* Convert RF5112 specific data to generic raw data + * used by interpolation code */ +static int +ath5k_eeprom_convert_pcal_info_5112(struct ath5k_hw *ah, int mode, + struct ath5k_chan_pcal_info *chinfo) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info_rf5112 *pcinfo; + u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; + unsigned int pier, pdg, point; + + /* Fill raw data for each calibration pier */ + for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { + + pcinfo = &chinfo[pier].rf5112_info; + + /* Allocate pd_curves for this cal pier */ + chinfo[pier].pd_curves = + kcalloc(AR5K_EEPROM_N_PD_CURVES, + sizeof(struct ath5k_pdgain_info), + GFP_KERNEL); + + if (!chinfo[pier].pd_curves) + return -ENOMEM; + + /* Fill pd_curves */ + for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) { + + u8 idx = pdgain_idx[pdg]; + struct ath5k_pdgain_info *pd = + &chinfo[pier].pd_curves[idx]; + + /* Lowest gain curve (max power) */ + if (pdg == 0) { + /* One more point for better accuracy */ + pd->pd_points = AR5K_EEPROM_N_XPD0_POINTS; + + /* Allocate pd points for this curve */ + pd->pd_step = kcalloc(pd->pd_points, + sizeof(u8), GFP_KERNEL); + + if (!pd->pd_step) + return -ENOMEM; + + pd->pd_pwr = kcalloc(pd->pd_points, + sizeof(s16), GFP_KERNEL); + + if (!pd->pd_pwr) + return -ENOMEM; + + + /* Fill raw dataset + * (all power levels are in 0.25dB units) */ + pd->pd_step[0] = pcinfo->pcdac_x0[0]; + pd->pd_pwr[0] = pcinfo->pwr_x0[0]; + + for (point = 1; point < pd->pd_points; + point++) { + /* Absolute values */ + pd->pd_pwr[point] = + pcinfo->pwr_x0[point]; + + /* Deltas */ + pd->pd_step[point] = + pd->pd_step[point - 1] + + pcinfo->pcdac_x0[point]; + } + + /* Set min power for this frequency */ + chinfo[pier].min_pwr = pd->pd_pwr[0]; + + /* Highest gain curve (min power) */ + } else if (pdg == 1) { + + pd->pd_points = AR5K_EEPROM_N_XPD3_POINTS; + + /* Allocate pd points for this curve */ + pd->pd_step = kcalloc(pd->pd_points, + sizeof(u8), GFP_KERNEL); + + if (!pd->pd_step) + return -ENOMEM; + + pd->pd_pwr = kcalloc(pd->pd_points, + sizeof(s16), GFP_KERNEL); + + if (!pd->pd_pwr) + return -ENOMEM; + + /* Fill raw dataset + * (all power levels are in 0.25dB units) */ + for (point = 0; point < pd->pd_points; + point++) { + /* Absolute values */ + pd->pd_pwr[point] = + pcinfo->pwr_x3[point]; + + /* Fixed points */ + pd->pd_step[point] = + pcinfo->pcdac_x3[point]; + } + + /* Since we have a higher gain curve + * override min power */ + chinfo[pier].min_pwr = pd->pd_pwr[0]; + } + } + } + + return 0; +} + +/* Parse EEPROM data */ +static int +ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info; + struct ath5k_chan_pcal_info *gen_chan_info; + u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; + u32 offset; + u8 i, c; + u16 val; + int ret; + u8 pd_gains = 0; + + /* Count how many curves we have and + * identify them (which one of the 4 + * available curves we have on each count). + * Curves are stored from lower (x0) to + * higher (x3) gain */ + for (i = 0; i < AR5K_EEPROM_N_PD_CURVES; i++) { + /* ee_x_gain[mode] is x gain mask */ + if ((ee->ee_x_gain[mode] >> i) & 0x1) + pdgain_idx[pd_gains++] = i; + } + ee->ee_pd_gains[mode] = pd_gains; + + if (pd_gains == 0 || pd_gains > 2) + return -EINVAL; + + switch (mode) { + case AR5K_EEPROM_MODE_11A: + /* + * Read 5GHz EEPROM channels + */ + offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); + ath5k_eeprom_init_11a_pcal_freq(ah, offset); + + offset += AR5K_EEPROM_GROUP2_OFFSET; + gen_chan_info = ee->ee_pwr_cal_a; + break; + case AR5K_EEPROM_MODE_11B: + offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); + if (AR5K_EEPROM_HDR_11A(ee->ee_header)) + offset += AR5K_EEPROM_GROUP3_OFFSET; + + /* NB: frequency piers parsed during mode init */ + gen_chan_info = ee->ee_pwr_cal_b; + break; + case AR5K_EEPROM_MODE_11G: + offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); + if (AR5K_EEPROM_HDR_11A(ee->ee_header)) + offset += AR5K_EEPROM_GROUP4_OFFSET; + else if (AR5K_EEPROM_HDR_11B(ee->ee_header)) + offset += AR5K_EEPROM_GROUP2_OFFSET; + + /* NB: frequency piers parsed during mode init */ + gen_chan_info = ee->ee_pwr_cal_g; + break; + default: + return -EINVAL; + } + + for (i = 0; i < ee->ee_n_piers[mode]; i++) { + chan_pcal_info = &gen_chan_info[i].rf5112_info; + + /* Power values in quarter dB + * for the lower xpd gain curve + * (0 dBm -> higher output power) */ + for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) { + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pwr_x0[c] = (s8) (val & 0xff); + chan_pcal_info->pwr_x0[++c] = (s8) ((val >> 8) & 0xff); + } + + /* PCDAC steps + * corresponding to the above power + * measurements */ + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pcdac_x0[1] = (val & 0x1f); + chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f); + chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f); + + /* Power values in quarter dB + * for the higher xpd gain curve + * (18 dBm -> lower output power) */ + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pwr_x3[0] = (s8) (val & 0xff); + chan_pcal_info->pwr_x3[1] = (s8) ((val >> 8) & 0xff); + + AR5K_EEPROM_READ(offset++, val); + chan_pcal_info->pwr_x3[2] = (val & 0xff); + + /* PCDAC steps + * corresponding to the above power + * measurements (fixed) */ + chan_pcal_info->pcdac_x3[0] = 20; + chan_pcal_info->pcdac_x3[1] = 35; + chan_pcal_info->pcdac_x3[2] = 63; + + if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) { + chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0x3f); + + /* Last xpd0 power level is also channel maximum */ + gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3]; + } else { + chan_pcal_info->pcdac_x0[0] = 1; + gen_chan_info[i].max_pwr = (s8) ((val >> 8) & 0xff); + } + + } + + return ath5k_eeprom_convert_pcal_info_5112(ah, mode, gen_chan_info); +} + + +/* + * Read power calibration for RF2413 chips + * + * For RF2413 we have a Power to PDDAC table (Power Detector) + * instead of a PCDAC and 4 pd gain curves for each calibrated channel. + * Each curve has power on x axis in 0.5 db steps and PDDADC steps on y + * axis and looks like an exponential function like the RF5111 curve. + * + * To recreate the curves we read here the points and interpolate + * later. Note that in most cases only 2 (higher and lower) curves are + * used (like RF5112) but vendors have the oportunity to include all + * 4 curves on eeprom. The final curve (higher power) has an extra + * point for better accuracy like RF5112. + */ + +/* For RF2413 power calibration data doesn't start on a fixed location and + * if a mode is not supported, it's section is missing -not zeroed-. + * So we need to calculate the starting offset for each section by using + * these two functions */ + +/* Return the size of each section based on the mode and the number of pd + * gains available (maximum 4). */ +static inline unsigned int +ath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode) +{ + static const unsigned int pdgains_size[] = { 4, 6, 9, 12 }; + unsigned int sz; + + sz = pdgains_size[ee->ee_pd_gains[mode] - 1]; + sz *= ee->ee_n_piers[mode]; + + return sz; +} + +/* Return the starting offset for a section based on the modes supported + * and each section's size. */ +static unsigned int +ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) +{ + u32 offset = AR5K_EEPROM_CAL_DATA_START(ee->ee_misc4); + + switch(mode) { + case AR5K_EEPROM_MODE_11G: + if (AR5K_EEPROM_HDR_11B(ee->ee_header)) + offset += ath5k_pdgains_size_2413(ee, + AR5K_EEPROM_MODE_11B) + + AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; + /* fall through */ + case AR5K_EEPROM_MODE_11B: + if (AR5K_EEPROM_HDR_11A(ee->ee_header)) + offset += ath5k_pdgains_size_2413(ee, + AR5K_EEPROM_MODE_11A) + + AR5K_EEPROM_N_5GHZ_CHAN / 2; + /* fall through */ + case AR5K_EEPROM_MODE_11A: + break; + default: + break; + } + + return offset; +} + +/* Convert RF2413 specific data to generic raw data + * used by interpolation code */ +static int +ath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode, + struct ath5k_chan_pcal_info *chinfo) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info_rf2413 *pcinfo; + u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; + unsigned int pier, pdg, point; + + /* Fill raw data for each calibration pier */ + for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { + + pcinfo = &chinfo[pier].rf2413_info; + + /* Allocate pd_curves for this cal pier */ + chinfo[pier].pd_curves = + kcalloc(AR5K_EEPROM_N_PD_CURVES, + sizeof(struct ath5k_pdgain_info), + GFP_KERNEL); + + if (!chinfo[pier].pd_curves) + return -ENOMEM; + + /* Fill pd_curves */ + for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) { + + u8 idx = pdgain_idx[pdg]; + struct ath5k_pdgain_info *pd = + &chinfo[pier].pd_curves[idx]; + + /* One more point for the highest power + * curve (lowest gain) */ + if (pdg == ee->ee_pd_gains[mode] - 1) + pd->pd_points = AR5K_EEPROM_N_PD_POINTS; + else + pd->pd_points = AR5K_EEPROM_N_PD_POINTS - 1; + + /* Allocate pd points for this curve */ + pd->pd_step = kcalloc(pd->pd_points, + sizeof(u8), GFP_KERNEL); + + if (!pd->pd_step) + return -ENOMEM; + + pd->pd_pwr = kcalloc(pd->pd_points, + sizeof(s16), GFP_KERNEL); + + if (!pd->pd_pwr) + return -ENOMEM; + + /* Fill raw dataset + * convert all pwr levels to + * quarter dB for RF5112 combatibility */ + pd->pd_step[0] = pcinfo->pddac_i[pdg]; + pd->pd_pwr[0] = 4 * pcinfo->pwr_i[pdg]; + + for (point = 1; point < pd->pd_points; point++) { + + pd->pd_pwr[point] = pd->pd_pwr[point - 1] + + 2 * pcinfo->pwr[pdg][point - 1]; + + pd->pd_step[point] = pd->pd_step[point - 1] + + pcinfo->pddac[pdg][point - 1]; + + } + + /* Highest gain curve -> min power */ + if (pdg == 0) + chinfo[pier].min_pwr = pd->pd_pwr[0]; + + /* Lowest gain curve -> max power */ + if (pdg == ee->ee_pd_gains[mode] - 1) + chinfo[pier].max_pwr = + pd->pd_pwr[pd->pd_points - 1]; + } + } + + return 0; +} + +/* Parse EEPROM data */ +static int +ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info_rf2413 *pcinfo; + struct ath5k_chan_pcal_info *chinfo; + u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; + u32 offset; + int idx, i, ret; + u16 val; + u8 pd_gains = 0; + + /* Count how many curves we have and + * identify them (which one of the 4 + * available curves we have on each count). + * Curves are stored from higher to + * lower gain so we go backwards */ + for (idx = AR5K_EEPROM_N_PD_CURVES - 1; idx >= 0; idx--) { + /* ee_x_gain[mode] is x gain mask */ + if ((ee->ee_x_gain[mode] >> idx) & 0x1) + pdgain_idx[pd_gains++] = idx; + + } + ee->ee_pd_gains[mode] = pd_gains; + + if (pd_gains == 0) + return -EINVAL; + + offset = ath5k_cal_data_offset_2413(ee, mode); + switch (mode) { + case AR5K_EEPROM_MODE_11A: + if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) + return 0; + + ath5k_eeprom_init_11a_pcal_freq(ah, offset); + offset += AR5K_EEPROM_N_5GHZ_CHAN / 2; + chinfo = ee->ee_pwr_cal_a; + break; + case AR5K_EEPROM_MODE_11B: + if (!AR5K_EEPROM_HDR_11B(ee->ee_header)) + return 0; + + ath5k_eeprom_init_11bg_2413(ah, mode, offset); + offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; + chinfo = ee->ee_pwr_cal_b; + break; + case AR5K_EEPROM_MODE_11G: + if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) + return 0; + + ath5k_eeprom_init_11bg_2413(ah, mode, offset); + offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; + chinfo = ee->ee_pwr_cal_g; + break; + default: + return -EINVAL; + } + + for (i = 0; i < ee->ee_n_piers[mode]; i++) { + pcinfo = &chinfo[i].rf2413_info; + + /* + * Read pwr_i, pddac_i and the first + * 2 pd points (pwr, pddac) + */ + AR5K_EEPROM_READ(offset++, val); + pcinfo->pwr_i[0] = val & 0x1f; + pcinfo->pddac_i[0] = (val >> 5) & 0x7f; + pcinfo->pwr[0][0] = (val >> 12) & 0xf; + + AR5K_EEPROM_READ(offset++, val); + pcinfo->pddac[0][0] = val & 0x3f; + pcinfo->pwr[0][1] = (val >> 6) & 0xf; + pcinfo->pddac[0][1] = (val >> 10) & 0x3f; + + AR5K_EEPROM_READ(offset++, val); + pcinfo->pwr[0][2] = val & 0xf; + pcinfo->pddac[0][2] = (val >> 4) & 0x3f; + + pcinfo->pwr[0][3] = 0; + pcinfo->pddac[0][3] = 0; + + if (pd_gains > 1) { + /* + * Pd gain 0 is not the last pd gain + * so it only has 2 pd points. + * Continue wih pd gain 1. + */ + pcinfo->pwr_i[1] = (val >> 10) & 0x1f; + + pcinfo->pddac_i[1] = (val >> 15) & 0x1; + AR5K_EEPROM_READ(offset++, val); + pcinfo->pddac_i[1] |= (val & 0x3F) << 1; + + pcinfo->pwr[1][0] = (val >> 6) & 0xf; + pcinfo->pddac[1][0] = (val >> 10) & 0x3f; + + AR5K_EEPROM_READ(offset++, val); + pcinfo->pwr[1][1] = val & 0xf; + pcinfo->pddac[1][1] = (val >> 4) & 0x3f; + pcinfo->pwr[1][2] = (val >> 10) & 0xf; + + pcinfo->pddac[1][2] = (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + pcinfo->pddac[1][2] |= (val & 0xF) << 2; + + pcinfo->pwr[1][3] = 0; + pcinfo->pddac[1][3] = 0; + } else if (pd_gains == 1) { + /* + * Pd gain 0 is the last one so + * read the extra point. + */ + pcinfo->pwr[0][3] = (val >> 10) & 0xf; + + pcinfo->pddac[0][3] = (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + pcinfo->pddac[0][3] |= (val & 0xF) << 2; + } + + /* + * Proceed with the other pd_gains + * as above. + */ + if (pd_gains > 2) { + pcinfo->pwr_i[2] = (val >> 4) & 0x1f; + pcinfo->pddac_i[2] = (val >> 9) & 0x7f; + + AR5K_EEPROM_READ(offset++, val); + pcinfo->pwr[2][0] = (val >> 0) & 0xf; + pcinfo->pddac[2][0] = (val >> 4) & 0x3f; + pcinfo->pwr[2][1] = (val >> 10) & 0xf; + + pcinfo->pddac[2][1] = (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + pcinfo->pddac[2][1] |= (val & 0xF) << 2; + + pcinfo->pwr[2][2] = (val >> 4) & 0xf; + pcinfo->pddac[2][2] = (val >> 8) & 0x3f; + + pcinfo->pwr[2][3] = 0; + pcinfo->pddac[2][3] = 0; + } else if (pd_gains == 2) { + pcinfo->pwr[1][3] = (val >> 4) & 0xf; + pcinfo->pddac[1][3] = (val >> 8) & 0x3f; + } + + if (pd_gains > 3) { + pcinfo->pwr_i[3] = (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + pcinfo->pwr_i[3] |= ((val >> 0) & 0x7) << 2; + + pcinfo->pddac_i[3] = (val >> 3) & 0x7f; + pcinfo->pwr[3][0] = (val >> 10) & 0xf; + pcinfo->pddac[3][0] = (val >> 14) & 0x3; + + AR5K_EEPROM_READ(offset++, val); + pcinfo->pddac[3][0] |= (val & 0xF) << 2; + pcinfo->pwr[3][1] = (val >> 4) & 0xf; + pcinfo->pddac[3][1] = (val >> 8) & 0x3f; + + pcinfo->pwr[3][2] = (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + pcinfo->pwr[3][2] |= ((val >> 0) & 0x3) << 2; + + pcinfo->pddac[3][2] = (val >> 2) & 0x3f; + pcinfo->pwr[3][3] = (val >> 8) & 0xf; + + pcinfo->pddac[3][3] = (val >> 12) & 0xF; + AR5K_EEPROM_READ(offset++, val); + pcinfo->pddac[3][3] |= ((val >> 0) & 0x3) << 4; + } else if (pd_gains == 3) { + pcinfo->pwr[2][3] = (val >> 14) & 0x3; + AR5K_EEPROM_READ(offset++, val); + pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2; + + pcinfo->pddac[2][3] = (val >> 2) & 0x3f; + } + } + + return ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo); +} + + +/* + * Read per rate target power (this is the maximum tx power + * supported by the card). This info is used when setting + * tx power, no matter the channel. + * + * This also works for v5 EEPROMs. + */ +static int +ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_rate_pcal_info *rate_pcal_info; + u8 *rate_target_pwr_num; + u32 offset; + u16 val; + int ret, i; + + offset = AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1); + rate_target_pwr_num = &ee->ee_rate_target_pwr_num[mode]; + switch (mode) { + case AR5K_EEPROM_MODE_11A: + offset += AR5K_EEPROM_TARGET_PWR_OFF_11A(ee->ee_version); + rate_pcal_info = ee->ee_rate_tpwr_a; + ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_CHAN; + break; + case AR5K_EEPROM_MODE_11B: + offset += AR5K_EEPROM_TARGET_PWR_OFF_11B(ee->ee_version); + rate_pcal_info = ee->ee_rate_tpwr_b; + ee->ee_rate_target_pwr_num[mode] = 2; /* 3rd is g mode's 1st */ + break; + case AR5K_EEPROM_MODE_11G: + offset += AR5K_EEPROM_TARGET_PWR_OFF_11G(ee->ee_version); + rate_pcal_info = ee->ee_rate_tpwr_g; + ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_2GHZ_CHAN; + break; + default: + return -EINVAL; + } + + /* Different freq mask for older eeproms (<= v3.2) */ + if (ee->ee_version <= AR5K_EEPROM_VERSION_3_2) { + for (i = 0; i < (*rate_target_pwr_num); i++) { + AR5K_EEPROM_READ(offset++, val); + rate_pcal_info[i].freq = + ath5k_eeprom_bin2freq(ee, (val >> 9) & 0x7f, mode); + + rate_pcal_info[i].target_power_6to24 = ((val >> 3) & 0x3f); + rate_pcal_info[i].target_power_36 = (val << 3) & 0x3f; + + AR5K_EEPROM_READ(offset++, val); + + if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS || + val == 0) { + (*rate_target_pwr_num) = i; + break; + } + + rate_pcal_info[i].target_power_36 |= ((val >> 13) & 0x7); + rate_pcal_info[i].target_power_48 = ((val >> 7) & 0x3f); + rate_pcal_info[i].target_power_54 = ((val >> 1) & 0x3f); + } + } else { + for (i = 0; i < (*rate_target_pwr_num); i++) { + AR5K_EEPROM_READ(offset++, val); + rate_pcal_info[i].freq = + ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); + + rate_pcal_info[i].target_power_6to24 = ((val >> 2) & 0x3f); + rate_pcal_info[i].target_power_36 = (val << 4) & 0x3f; + + AR5K_EEPROM_READ(offset++, val); + + if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS || + val == 0) { + (*rate_target_pwr_num) = i; + break; + } + + rate_pcal_info[i].target_power_36 |= (val >> 12) & 0xf; + rate_pcal_info[i].target_power_48 = ((val >> 6) & 0x3f); + rate_pcal_info[i].target_power_54 = (val & 0x3f); + } + } + + return 0; +} + +/* + * Read per channel calibration info from EEPROM + * + * This info is used to calibrate the baseband power table. Imagine + * that for each channel there is a power curve that's hw specific + * (depends on amplifier etc) and we try to "correct" this curve using + * offests we pass on to phy chip (baseband -> before amplifier) so that + * it can use accurate power values when setting tx power (takes amplifier's + * performance on each channel into account). + * + * EEPROM provides us with the offsets for some pre-calibrated channels + * and we have to interpolate to create the full table for these channels and + * also the table for any channel. + */ +static int +ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + int (*read_pcal)(struct ath5k_hw *hw, int mode); + int mode; + int err; + + if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) && + (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 1)) + read_pcal = ath5k_eeprom_read_pcal_info_5112; + else if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0) && + (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 2)) + read_pcal = ath5k_eeprom_read_pcal_info_2413; + else + read_pcal = ath5k_eeprom_read_pcal_info_5111; + + + for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; + mode++) { + err = read_pcal(ah, mode); + if (err) + return err; + + err = ath5k_eeprom_read_target_rate_pwr_info(ah, mode); + if (err < 0) + return err; + } + + return 0; +} + +static int +ath5k_eeprom_free_pcal_info(struct ath5k_hw *ah, int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info *chinfo; + u8 pier, pdg; + + switch (mode) { + case AR5K_EEPROM_MODE_11A: + if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) + return 0; + chinfo = ee->ee_pwr_cal_a; + break; + case AR5K_EEPROM_MODE_11B: + if (!AR5K_EEPROM_HDR_11B(ee->ee_header)) + return 0; + chinfo = ee->ee_pwr_cal_b; + break; + case AR5K_EEPROM_MODE_11G: + if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) + return 0; + chinfo = ee->ee_pwr_cal_g; + break; + default: + return -EINVAL; + } + + for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { + if (!chinfo[pier].pd_curves) + continue; + + for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) { + struct ath5k_pdgain_info *pd = + &chinfo[pier].pd_curves[pdg]; + + if (pd != NULL) { + kfree(pd->pd_step); + kfree(pd->pd_pwr); + } + } + + kfree(chinfo[pier].pd_curves); + } + + return 0; +} + +void +ath5k_eeprom_detach(struct ath5k_hw *ah) +{ + u8 mode; + + for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) + ath5k_eeprom_free_pcal_info(ah, mode); +} + +/* Read conformance test limits used for regulatory control */ +static int +ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_edge_power *rep; + unsigned int fmask, pmask; + unsigned int ctl_mode; + int ret, i, j; + u32 offset; + u16 val; + + pmask = AR5K_EEPROM_POWER_M; + fmask = AR5K_EEPROM_FREQ_M(ee->ee_version); + offset = AR5K_EEPROM_CTL(ee->ee_version); + ee->ee_ctls = AR5K_EEPROM_N_CTLS(ee->ee_version); + for (i = 0; i < ee->ee_ctls; i += 2) { + AR5K_EEPROM_READ(offset++, val); + ee->ee_ctl[i] = (val >> 8) & 0xff; + ee->ee_ctl[i + 1] = val & 0xff; + } + + offset = AR5K_EEPROM_GROUP8_OFFSET; + if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0) + offset += AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1) - + AR5K_EEPROM_GROUP5_OFFSET; + else + offset += AR5K_EEPROM_GROUPS_START(ee->ee_version); + + rep = ee->ee_ctl_pwr; + for(i = 0; i < ee->ee_ctls; i++) { + switch(ee->ee_ctl[i] & AR5K_CTL_MODE_M) { + case AR5K_CTL_11A: + case AR5K_CTL_TURBO: + ctl_mode = AR5K_EEPROM_MODE_11A; + break; + default: + ctl_mode = AR5K_EEPROM_MODE_11G; + break; + } + if (ee->ee_ctl[i] == 0) { + if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) + offset += 8; + else + offset += 7; + rep += AR5K_EEPROM_N_EDGES; + continue; + } + if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) { + for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) { + AR5K_EEPROM_READ(offset++, val); + rep[j].freq = (val >> 8) & fmask; + rep[j + 1].freq = val & fmask; + } + for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) { + AR5K_EEPROM_READ(offset++, val); + rep[j].edge = (val >> 8) & pmask; + rep[j].flag = (val >> 14) & 1; + rep[j + 1].edge = val & pmask; + rep[j + 1].flag = (val >> 6) & 1; + } + } else { + AR5K_EEPROM_READ(offset++, val); + rep[0].freq = (val >> 9) & fmask; + rep[1].freq = (val >> 2) & fmask; + rep[2].freq = (val << 5) & fmask; + + AR5K_EEPROM_READ(offset++, val); + rep[2].freq |= (val >> 11) & 0x1f; + rep[3].freq = (val >> 4) & fmask; + rep[4].freq = (val << 3) & fmask; + + AR5K_EEPROM_READ(offset++, val); + rep[4].freq |= (val >> 13) & 0x7; + rep[5].freq = (val >> 6) & fmask; + rep[6].freq = (val << 1) & fmask; + + AR5K_EEPROM_READ(offset++, val); + rep[6].freq |= (val >> 15) & 0x1; + rep[7].freq = (val >> 8) & fmask; + + rep[0].edge = (val >> 2) & pmask; + rep[1].edge = (val << 4) & pmask; + + AR5K_EEPROM_READ(offset++, val); + rep[1].edge |= (val >> 12) & 0xf; + rep[2].edge = (val >> 6) & pmask; + rep[3].edge = val & pmask; + + AR5K_EEPROM_READ(offset++, val); + rep[4].edge = (val >> 10) & pmask; + rep[5].edge = (val >> 4) & pmask; + rep[6].edge = (val << 2) & pmask; + + AR5K_EEPROM_READ(offset++, val); + rep[6].edge |= (val >> 14) & 0x3; + rep[7].edge = (val >> 8) & pmask; + } + for (j = 0; j < AR5K_EEPROM_N_EDGES; j++) { + rep[j].freq = ath5k_eeprom_bin2freq(ee, + rep[j].freq, ctl_mode); + } + rep += AR5K_EEPROM_N_EDGES; + } + + return 0; +} + +static int +ath5k_eeprom_read_spur_chans(struct ath5k_hw *ah) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u32 offset; + u16 val; + int ret = 0, i; + + offset = AR5K_EEPROM_CTL(ee->ee_version) + + AR5K_EEPROM_N_CTLS(ee->ee_version); + + if (ee->ee_version < AR5K_EEPROM_VERSION_5_3) { + /* No spur info for 5GHz */ + ee->ee_spur_chans[0][0] = AR5K_EEPROM_NO_SPUR; + /* 2 channels for 2GHz (2464/2420) */ + ee->ee_spur_chans[0][1] = AR5K_EEPROM_5413_SPUR_CHAN_1; + ee->ee_spur_chans[1][1] = AR5K_EEPROM_5413_SPUR_CHAN_2; + ee->ee_spur_chans[2][1] = AR5K_EEPROM_NO_SPUR; + } else if (ee->ee_version >= AR5K_EEPROM_VERSION_5_3) { + for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) { + AR5K_EEPROM_READ(offset, val); + ee->ee_spur_chans[i][0] = val; + AR5K_EEPROM_READ(offset + AR5K_EEPROM_N_SPUR_CHANS, + val); + ee->ee_spur_chans[i][1] = val; + offset++; + } + } + + return ret; +} + +/* + * Initialize eeprom data structure + */ +int +ath5k_eeprom_init(struct ath5k_hw *ah) +{ + int err; + + err = ath5k_eeprom_init_header(ah); + if (err < 0) + return err; + + err = ath5k_eeprom_init_modes(ah); + if (err < 0) + return err; + + err = ath5k_eeprom_read_pcal_info(ah); + if (err < 0) + return err; + + err = ath5k_eeprom_read_ctl_info(ah); + if (err < 0) + return err; + + err = ath5k_eeprom_read_spur_chans(ah); + if (err < 0) + return err; + + return 0; +} + +/* + * Read the MAC address from eeprom + */ +int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) +{ + u8 mac_d[ETH_ALEN] = {}; + u32 total, offset; + u16 data; + int octet, ret; + + ret = ath5k_hw_eeprom_read(ah, 0x20, &data); + if (ret) + return ret; + + for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) { + ret = ath5k_hw_eeprom_read(ah, offset, &data); + if (ret) + return ret; + + total += data; + mac_d[octet + 1] = data & 0xff; + mac_d[octet] = data >> 8; + octet += 2; + } + + if (!total || total == 3 * 0xffff) + return -EINVAL; + + memcpy(mac, mac_d, ETH_ALEN); + + return 0; +} diff --git a/drivers/net/wireless/ath/ath5k/eeprom.h b/drivers/net/wireless/ath/ath5k/eeprom.h new file mode 100644 index 00000000000..64be73a5eda --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/eeprom.h @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +/* + * Common ar5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE) + */ +#define AR5K_EEPROM_MAGIC 0x003d /* EEPROM Magic number */ +#define AR5K_EEPROM_MAGIC_VALUE 0x5aa5 /* Default - found on EEPROM */ +#define AR5K_EEPROM_MAGIC_5212 0x0000145c /* 5212 */ +#define AR5K_EEPROM_MAGIC_5211 0x0000145b /* 5211 */ +#define AR5K_EEPROM_MAGIC_5210 0x0000145a /* 5210 */ + +#define AR5K_EEPROM_IS_HB63 0x000b /* Talon detect */ + +#define AR5K_EEPROM_RFKILL 0x0f +#define AR5K_EEPROM_RFKILL_GPIO_SEL 0x0000001c +#define AR5K_EEPROM_RFKILL_GPIO_SEL_S 2 +#define AR5K_EEPROM_RFKILL_POLARITY 0x00000002 +#define AR5K_EEPROM_RFKILL_POLARITY_S 1 + +#define AR5K_EEPROM_REG_DOMAIN 0x00bf /* EEPROM regdom */ +#define AR5K_EEPROM_CHECKSUM 0x00c0 /* EEPROM checksum */ +#define AR5K_EEPROM_INFO_BASE 0x00c0 /* EEPROM header */ +#define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE) +#define AR5K_EEPROM_INFO_CKSUM 0xffff +#define AR5K_EEPROM_INFO(_n) (AR5K_EEPROM_INFO_BASE + (_n)) + +#define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1) /* EEPROM Version */ +#define AR5K_EEPROM_VERSION_3_0 0x3000 /* No idea what's going on before this version */ +#define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2Ghz (ar5211_rfregs) */ +#define AR5K_EEPROM_VERSION_3_2 0x3002 /* different frequency representation (eeprom_bin2freq) */ +#define AR5K_EEPROM_VERSION_3_3 0x3003 /* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */ +#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain, ee_cck_ofdm_power_delta (eeprom_read_modes) */ +#define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */ +#define AR5K_EEPROM_VERSION_4_1 0x4001 /* has ee_margin_tx_rx (eeprom_init) */ +#define AR5K_EEPROM_VERSION_4_2 0x4002 /* has ee_cck_ofdm_gain_delta (eeprom_init) */ +#define AR5K_EEPROM_VERSION_4_3 0x4003 /* power calibration changes */ +#define AR5K_EEPROM_VERSION_4_4 0x4004 +#define AR5K_EEPROM_VERSION_4_5 0x4005 +#define AR5K_EEPROM_VERSION_4_6 0x4006 /* has ee_scaled_cck_delta */ +#define AR5K_EEPROM_VERSION_4_7 0x3007 /* 4007 ? */ +#define AR5K_EEPROM_VERSION_4_9 0x4009 /* EAR futureproofing */ +#define AR5K_EEPROM_VERSION_5_0 0x5000 /* Has 2413 PDADC calibration etc */ +#define AR5K_EEPROM_VERSION_5_1 0x5001 /* Has capability values */ +#define AR5K_EEPROM_VERSION_5_3 0x5003 /* Has spur mitigation tables */ + +#define AR5K_EEPROM_MODE_11A 0 +#define AR5K_EEPROM_MODE_11B 1 +#define AR5K_EEPROM_MODE_11G 2 + +#define AR5K_EEPROM_HDR AR5K_EEPROM_INFO(2) /* Header that contains the device caps */ +#define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1) +#define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1) +#define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1) +#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2Ghz (?) */ +#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for a/XR mode (eeprom_init) */ +#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7) +#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */ +#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz */ + +/* Newer EEPROMs are using a different offset */ +#define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \ + (((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0) + +#define AR5K_EEPROM_ANT_GAIN(_v) AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3) +#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((s8)(((_v) >> 8) & 0xff)) +#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((s8)((_v) & 0xff)) + +/* Misc values available since EEPROM 4.0 */ +#define AR5K_EEPROM_MISC0 AR5K_EEPROM_INFO(4) +#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff) +#define AR5K_EEPROM_HDR_XR2_DIS(_v) (((_v) >> 12) & 0x1) +#define AR5K_EEPROM_HDR_XR5_DIS(_v) (((_v) >> 13) & 0x1) +#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3) + +#define AR5K_EEPROM_MISC1 AR5K_EEPROM_INFO(5) +#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff) +#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1) +#define AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(_v) (((_v) >> 15) & 0x1) + +#define AR5K_EEPROM_MISC2 AR5K_EEPROM_INFO(6) +#define AR5K_EEPROM_EEP_FILE_VERSION(_v) (((_v) >> 8) & 0xff) +#define AR5K_EEPROM_EAR_FILE_VERSION(_v) ((_v) & 0xff) + +#define AR5K_EEPROM_MISC3 AR5K_EEPROM_INFO(7) +#define AR5K_EEPROM_ART_BUILD_NUM(_v) (((_v) >> 10) & 0x3f) +#define AR5K_EEPROM_EAR_FILE_ID(_v) ((_v) & 0xff) + +#define AR5K_EEPROM_MISC4 AR5K_EEPROM_INFO(8) +#define AR5K_EEPROM_CAL_DATA_START(_v) (((_v) >> 4) & 0xfff) +#define AR5K_EEPROM_MASK_R0(_v) (((_v) >> 2) & 0x3) +#define AR5K_EEPROM_MASK_R1(_v) ((_v) & 0x3) + +#define AR5K_EEPROM_MISC5 AR5K_EEPROM_INFO(9) +#define AR5K_EEPROM_COMP_DIS(_v) ((_v) & 0x1) +#define AR5K_EEPROM_AES_DIS(_v) (((_v) >> 1) & 0x1) +#define AR5K_EEPROM_FF_DIS(_v) (((_v) >> 2) & 0x1) +#define AR5K_EEPROM_BURST_DIS(_v) (((_v) >> 3) & 0x1) +#define AR5K_EEPROM_MAX_QCU(_v) (((_v) >> 4) & 0xf) +#define AR5K_EEPROM_HEAVY_CLIP_EN(_v) (((_v) >> 8) & 0x1) +#define AR5K_EEPROM_KEY_CACHE_SIZE(_v) (((_v) >> 12) & 0xf) + +#define AR5K_EEPROM_MISC6 AR5K_EEPROM_INFO(10) +#define AR5K_EEPROM_TX_CHAIN_DIS ((_v) & 0x8) +#define AR5K_EEPROM_RX_CHAIN_DIS (((_v) >> 3) & 0x8) +#define AR5K_EEPROM_FCC_MID_EN (((_v) >> 6) & 0x1) +#define AR5K_EEPROM_JAP_U1EVEN_EN (((_v) >> 7) & 0x1) +#define AR5K_EEPROM_JAP_U2_EN (((_v) >> 8) & 0x1) +#define AR5K_EEPROM_JAP_U1ODD_EN (((_v) >> 9) & 0x1) +#define AR5K_EEPROM_JAP_11A_NEW_EN (((_v) >> 10) & 0x1) + +/* calibration settings */ +#define AR5K_EEPROM_MODES_11A(_v) AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4) +#define AR5K_EEPROM_MODES_11B(_v) AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2) +#define AR5K_EEPROM_MODES_11G(_v) AR5K_EEPROM_OFF(_v, 0x00da, 0x010d) +#define AR5K_EEPROM_CTL(_v) AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128) /* Conformance test limits */ +#define AR5K_EEPROM_GROUPS_START(_v) AR5K_EEPROM_OFF(_v, 0x0100, 0x0150) /* Start of Groups */ +#define AR5K_EEPROM_GROUP1_OFFSET 0x0 +#define AR5K_EEPROM_GROUP2_OFFSET 0x5 +#define AR5K_EEPROM_GROUP3_OFFSET 0x37 +#define AR5K_EEPROM_GROUP4_OFFSET 0x46 +#define AR5K_EEPROM_GROUP5_OFFSET 0x55 +#define AR5K_EEPROM_GROUP6_OFFSET 0x65 +#define AR5K_EEPROM_GROUP7_OFFSET 0x69 +#define AR5K_EEPROM_GROUP8_OFFSET 0x6f + +#define AR5K_EEPROM_TARGET_PWR_OFF_11A(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \ + AR5K_EEPROM_GROUP5_OFFSET, 0x0000) +#define AR5K_EEPROM_TARGET_PWR_OFF_11B(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \ + AR5K_EEPROM_GROUP6_OFFSET, 0x0010) +#define AR5K_EEPROM_TARGET_PWR_OFF_11G(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \ + AR5K_EEPROM_GROUP7_OFFSET, 0x0014) + +/* [3.1 - 3.3] */ +#define AR5K_EEPROM_OBDB0_2GHZ 0x00ec +#define AR5K_EEPROM_OBDB1_2GHZ 0x00ed + +#define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */ +#define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */ +#define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */ +#define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */ +#define AR5K_EEPROM_PROTECT_WR_32_63 0x0008 +#define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */ +#define AR5K_EEPROM_PROTECT_WR_64_127 0x0020 +#define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */ +#define AR5K_EEPROM_PROTECT_WR_128_191 0x0080 +#define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */ +#define AR5K_EEPROM_PROTECT_WR_192_207 0x0200 +#define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */ +#define AR5K_EEPROM_PROTECT_WR_208_223 0x0800 +#define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */ +#define AR5K_EEPROM_PROTECT_WR_224_239 0x2000 +#define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */ +#define AR5K_EEPROM_PROTECT_WR_240_255 0x8000 + +/* Some EEPROM defines */ +#define AR5K_EEPROM_EEP_SCALE 100 +#define AR5K_EEPROM_EEP_DELTA 10 +#define AR5K_EEPROM_N_MODES 3 +#define AR5K_EEPROM_N_5GHZ_CHAN 10 +#define AR5K_EEPROM_N_2GHZ_CHAN 3 +#define AR5K_EEPROM_N_2GHZ_CHAN_2413 4 +#define AR5K_EEPROM_N_2GHZ_CHAN_MAX 4 +#define AR5K_EEPROM_MAX_CHAN 10 +#define AR5K_EEPROM_N_PWR_POINTS_5111 11 +#define AR5K_EEPROM_N_PCDAC 11 +#define AR5K_EEPROM_N_PHASE_CAL 5 +#define AR5K_EEPROM_N_TEST_FREQ 8 +#define AR5K_EEPROM_N_EDGES 8 +#define AR5K_EEPROM_N_INTERCEPTS 11 +#define AR5K_EEPROM_FREQ_M(_v) AR5K_EEPROM_OFF(_v, 0x7f, 0xff) +#define AR5K_EEPROM_PCDAC_M 0x3f +#define AR5K_EEPROM_PCDAC_START 1 +#define AR5K_EEPROM_PCDAC_STOP 63 +#define AR5K_EEPROM_PCDAC_STEP 1 +#define AR5K_EEPROM_NON_EDGE_M 0x40 +#define AR5K_EEPROM_CHANNEL_POWER 8 +#define AR5K_EEPROM_N_OBDB 4 +#define AR5K_EEPROM_OBDB_DIS 0xffff +#define AR5K_EEPROM_CHANNEL_DIS 0xff +#define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10) +#define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32) +#define AR5K_EEPROM_MAX_CTLS 32 +#define AR5K_EEPROM_N_PD_CURVES 4 +#define AR5K_EEPROM_N_XPD0_POINTS 4 +#define AR5K_EEPROM_N_XPD3_POINTS 3 +#define AR5K_EEPROM_N_PD_GAINS 4 +#define AR5K_EEPROM_N_PD_POINTS 5 +#define AR5K_EEPROM_N_INTERCEPT_10_2GHZ 35 +#define AR5K_EEPROM_N_INTERCEPT_10_5GHZ 55 +#define AR5K_EEPROM_POWER_M 0x3f +#define AR5K_EEPROM_POWER_MIN 0 +#define AR5K_EEPROM_POWER_MAX 3150 +#define AR5K_EEPROM_POWER_STEP 50 +#define AR5K_EEPROM_POWER_TABLE_SIZE 64 +#define AR5K_EEPROM_N_POWER_LOC_11B 4 +#define AR5K_EEPROM_N_POWER_LOC_11G 6 +#define AR5K_EEPROM_I_GAIN 10 +#define AR5K_EEPROM_CCK_OFDM_DELTA 15 +#define AR5K_EEPROM_N_IQ_CAL 2 +/* 5GHz/2GHz */ +enum ath5k_eeprom_freq_bands{ + AR5K_EEPROM_BAND_5GHZ = 0, + AR5K_EEPROM_BAND_2GHZ = 1, + AR5K_EEPROM_N_FREQ_BANDS, +}; +/* Spur chans per freq band */ +#define AR5K_EEPROM_N_SPUR_CHANS 5 +/* fbin value for chan 2464 x2 */ +#define AR5K_EEPROM_5413_SPUR_CHAN_1 1640 +/* fbin value for chan 2420 x2 */ +#define AR5K_EEPROM_5413_SPUR_CHAN_2 1200 +#define AR5K_EEPROM_SPUR_CHAN_MASK 0x3FFF +#define AR5K_EEPROM_NO_SPUR 0x8000 +#define AR5K_SPUR_CHAN_WIDTH 87 +#define AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz 3125 +#define AR5K_SPUR_SYMBOL_WIDTH_TURBO_100Hz 6250 + +#define AR5K_EEPROM_READ(_o, _v) do { \ + ret = ath5k_hw_eeprom_read(ah, (_o), &(_v)); \ + if (ret) \ + return ret; \ +} while (0) + +#define AR5K_EEPROM_READ_HDR(_o, _v) \ + AR5K_EEPROM_READ(_o, ah->ah_capabilities.cap_eeprom._v); \ + +enum ath5k_ant_table { + AR5K_ANT_CTL = 0, /* Idle switch table settings */ + AR5K_ANT_SWTABLE_A = 1, /* Switch table for antenna A */ + AR5K_ANT_SWTABLE_B = 2, /* Switch table for antenna B */ + AR5K_ANT_MAX, +}; + +enum ath5k_ctl_mode { + AR5K_CTL_11A = 0, + AR5K_CTL_11B = 1, + AR5K_CTL_11G = 2, + AR5K_CTL_TURBO = 3, + AR5K_CTL_TURBOG = 4, + AR5K_CTL_2GHT20 = 5, + AR5K_CTL_5GHT20 = 6, + AR5K_CTL_2GHT40 = 7, + AR5K_CTL_5GHT40 = 8, + AR5K_CTL_MODE_M = 15, +}; + +/* Default CTL ids for the 3 main reg domains. + * Atheros only uses these by default but vendors + * can have up to 32 different CTLs for different + * scenarios. Note that theese values are ORed with + * the mode id (above) so we can have up to 24 CTL + * datasets out of these 3 main regdomains. That leaves + * 8 ids that can be used by vendors and since 0x20 is + * missing from HAL sources i guess this is the set of + * custom CTLs vendors can use. */ +#define AR5K_CTL_FCC 0x10 +#define AR5K_CTL_CUSTOM 0x20 +#define AR5K_CTL_ETSI 0x30 +#define AR5K_CTL_MKK 0x40 + +/* Indicates a CTL with only mode set and + * no reg domain mapping, such CTLs are used + * for world roaming domains or simply when + * a reg domain is not set */ +#define AR5K_CTL_NO_REGDOMAIN 0xf0 + +/* Indicates an empty (invalid) CTL */ +#define AR5K_CTL_NO_CTL 0xff + +/* Per channel calibration data, used for power table setup */ +struct ath5k_chan_pcal_info_rf5111 { + /* Power levels in half dbm units + * for one power curve. */ + u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111]; + /* PCDAC table steps + * for the above values */ + u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111]; + /* Starting PCDAC step */ + u8 pcdac_min; + /* Final PCDAC step */ + u8 pcdac_max; +}; + +struct ath5k_chan_pcal_info_rf5112 { + /* Power levels in quarter dBm units + * for lower (0) and higher (3) + * level curves in 0.25dB units */ + s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS]; + s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS]; + /* PCDAC table steps + * for the above values */ + u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS]; + u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS]; +}; + +struct ath5k_chan_pcal_info_rf2413 { + /* Starting pwr/pddac values */ + s8 pwr_i[AR5K_EEPROM_N_PD_GAINS]; + u8 pddac_i[AR5K_EEPROM_N_PD_GAINS]; + /* (pwr,pddac) points + * power levels in 0.5dB units */ + s8 pwr[AR5K_EEPROM_N_PD_GAINS] + [AR5K_EEPROM_N_PD_POINTS]; + u8 pddac[AR5K_EEPROM_N_PD_GAINS] + [AR5K_EEPROM_N_PD_POINTS]; +}; + +enum ath5k_powertable_type { + AR5K_PWRTABLE_PWR_TO_PCDAC = 0, + AR5K_PWRTABLE_LINEAR_PCDAC = 1, + AR5K_PWRTABLE_PWR_TO_PDADC = 2, +}; + +struct ath5k_pdgain_info { + u8 pd_points; + u8 *pd_step; + /* Power values are in + * 0.25dB units */ + s16 *pd_pwr; +}; + +struct ath5k_chan_pcal_info { + /* Frequency */ + u16 freq; + /* Tx power boundaries */ + s16 max_pwr; + s16 min_pwr; + union { + struct ath5k_chan_pcal_info_rf5111 rf5111_info; + struct ath5k_chan_pcal_info_rf5112 rf5112_info; + struct ath5k_chan_pcal_info_rf2413 rf2413_info; + }; + /* Raw values used by phy code + * Curves are stored in order from lower + * gain to higher gain (max txpower -> min txpower) */ + struct ath5k_pdgain_info *pd_curves; +}; + +/* Per rate calibration data for each mode, + * used for rate power table setup. + * Note: Values in 0.5dB units */ +struct ath5k_rate_pcal_info { + u16 freq; /* Frequency */ + /* Power level for 6-24Mbit/s rates or + * 1Mb rate */ + u16 target_power_6to24; + /* Power level for 36Mbit rate or + * 2Mb rate */ + u16 target_power_36; + /* Power level for 48Mbit rate or + * 5.5Mbit rate */ + u16 target_power_48; + /* Power level for 54Mbit rate or + * 11Mbit rate */ + u16 target_power_54; +}; + +/* Power edges for conformance test limits */ +struct ath5k_edge_power { + u16 freq; + u16 edge; /* in half dBm */ + bool flag; +}; + +/* EEPROM calibration data */ +struct ath5k_eeprom_info { + + /* Header information */ + u16 ee_magic; + u16 ee_protect; + u16 ee_regdomain; + u16 ee_version; + u16 ee_header; + u16 ee_ant_gain; + u8 ee_rfkill_pin; + bool ee_rfkill_pol; + bool ee_is_hb63; + u16 ee_misc0; + u16 ee_misc1; + u16 ee_misc2; + u16 ee_misc3; + u16 ee_misc4; + u16 ee_misc5; + u16 ee_misc6; + u16 ee_cck_ofdm_gain_delta; + u16 ee_cck_ofdm_power_delta; + u16 ee_scaled_cck_delta; + + /* RF Calibration settings (reset, rfregs) */ + u16 ee_i_cal[AR5K_EEPROM_N_MODES]; + u16 ee_q_cal[AR5K_EEPROM_N_MODES]; + u16 ee_fixed_bias[AR5K_EEPROM_N_MODES]; + u16 ee_turbo_max_power[AR5K_EEPROM_N_MODES]; + u16 ee_xr_power[AR5K_EEPROM_N_MODES]; + u16 ee_switch_settling[AR5K_EEPROM_N_MODES]; + u16 ee_atn_tx_rx[AR5K_EEPROM_N_MODES]; + u16 ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC]; + u16 ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB]; + u16 ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB]; + u16 ee_tx_end2xlna_enable[AR5K_EEPROM_N_MODES]; + u16 ee_tx_end2xpa_disable[AR5K_EEPROM_N_MODES]; + u16 ee_tx_frm2xpa_enable[AR5K_EEPROM_N_MODES]; + u16 ee_thr_62[AR5K_EEPROM_N_MODES]; + u16 ee_xlna_gain[AR5K_EEPROM_N_MODES]; + u16 ee_xpd[AR5K_EEPROM_N_MODES]; + u16 ee_x_gain[AR5K_EEPROM_N_MODES]; + u16 ee_i_gain[AR5K_EEPROM_N_MODES]; + u16 ee_margin_tx_rx[AR5K_EEPROM_N_MODES]; + u16 ee_switch_settling_turbo[AR5K_EEPROM_N_MODES]; + u16 ee_margin_tx_rx_turbo[AR5K_EEPROM_N_MODES]; + u16 ee_atn_tx_rx_turbo[AR5K_EEPROM_N_MODES]; + + /* Power calibration data */ + u16 ee_false_detect[AR5K_EEPROM_N_MODES]; + + /* Number of pd gain curves per mode */ + u8 ee_pd_gains[AR5K_EEPROM_N_MODES]; + /* Back mapping pdcurve number -> pdcurve index in pd->pd_curves */ + u8 ee_pdc_to_idx[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PD_GAINS]; + + u8 ee_n_piers[AR5K_EEPROM_N_MODES]; + struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN]; + struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; + struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; + + /* Per rate target power levels */ + u8 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES]; + struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN]; + struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; + struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; + + /* Conformance test limits (Unused) */ + u8 ee_ctls; + u8 ee_ctl[AR5K_EEPROM_MAX_CTLS]; + struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS]; + + /* Noise Floor Calibration settings */ + s16 ee_noise_floor_thr[AR5K_EEPROM_N_MODES]; + s8 ee_adc_desired_size[AR5K_EEPROM_N_MODES]; + s8 ee_pga_desired_size[AR5K_EEPROM_N_MODES]; + s8 ee_adc_desired_size_turbo[AR5K_EEPROM_N_MODES]; + s8 ee_pga_desired_size_turbo[AR5K_EEPROM_N_MODES]; + s8 ee_pd_gain_overlap; + + /* Spur mitigation data (fbin values for spur channels) */ + u16 ee_spur_chans[AR5K_EEPROM_N_SPUR_CHANS][AR5K_EEPROM_N_FREQ_BANDS]; + + /* Antenna raw switch tables */ + u32 ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; +}; + diff --git a/drivers/net/wireless/ath/ath5k/gpio.c b/drivers/net/wireless/ath/ath5k/gpio.c new file mode 100644 index 00000000000..64a27e73d02 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/gpio.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +/****************\ + GPIO Functions +\****************/ + +#include "ath5k.h" +#include "reg.h" +#include "debug.h" +#include "base.h" + +/* + * Set led state + */ +void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state) +{ + u32 led; + /*5210 has different led mode handling*/ + u32 led_5210; + + ATH5K_TRACE(ah->ah_sc); + + /*Reset led status*/ + if (ah->ah_version != AR5K_AR5210) + AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, + AR5K_PCICFG_LEDMODE | AR5K_PCICFG_LED); + else + AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_LED); + + /* + * Some blinking values, define at your wish + */ + switch (state) { + case AR5K_LED_SCAN: + case AR5K_LED_AUTH: + led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_PEND; + led_5210 = AR5K_PCICFG_LED_PEND | AR5K_PCICFG_LED_BCTL; + break; + + case AR5K_LED_INIT: + led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_NONE; + led_5210 = AR5K_PCICFG_LED_PEND; + break; + + case AR5K_LED_ASSOC: + case AR5K_LED_RUN: + led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_ASSOC; + led_5210 = AR5K_PCICFG_LED_ASSOC; + break; + + default: + led = AR5K_PCICFG_LEDMODE_PROM | AR5K_PCICFG_LED_NONE; + led_5210 = AR5K_PCICFG_LED_PEND; + break; + } + + /*Write new status to the register*/ + if (ah->ah_version != AR5K_AR5210) + AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led); + else + AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led_5210); +} + +/* + * Set GPIO inputs + */ +int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio) +{ + ATH5K_TRACE(ah->ah_sc); + if (gpio >= AR5K_NUM_GPIO) + return -EINVAL; + + ath5k_hw_reg_write(ah, + (ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio)) + | AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR); + + return 0; +} + +/* + * Set GPIO outputs + */ +int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio) +{ + ATH5K_TRACE(ah->ah_sc); + if (gpio >= AR5K_NUM_GPIO) + return -EINVAL; + + ath5k_hw_reg_write(ah, + (ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio)) + | AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR); + + return 0; +} + +/* + * Get GPIO state + */ +u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio) +{ + ATH5K_TRACE(ah->ah_sc); + if (gpio >= AR5K_NUM_GPIO) + return 0xffffffff; + + /* GPIO input magic */ + return ((ath5k_hw_reg_read(ah, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) & + 0x1; +} + +/* + * Set GPIO state + */ +int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val) +{ + u32 data; + ATH5K_TRACE(ah->ah_sc); + + if (gpio >= AR5K_NUM_GPIO) + return -EINVAL; + + /* GPIO output magic */ + data = ath5k_hw_reg_read(ah, AR5K_GPIODO); + + data &= ~(1 << gpio); + data |= (val & 1) << gpio; + + ath5k_hw_reg_write(ah, data, AR5K_GPIODO); + + return 0; +} + +/* + * Initialize the GPIO interrupt (RFKill switch) + */ +void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio, + u32 interrupt_level) +{ + u32 data; + + ATH5K_TRACE(ah->ah_sc); + if (gpio >= AR5K_NUM_GPIO) + return; + + /* + * Set the GPIO interrupt + */ + data = (ath5k_hw_reg_read(ah, AR5K_GPIOCR) & + ~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH | + AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) | + (AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA); + + ath5k_hw_reg_write(ah, interrupt_level ? data : + (data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR); + + ah->ah_imr |= AR5K_IMR_GPIO; + + /* Enable GPIO interrupts */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PIMR, AR5K_IMR_GPIO); +} + diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c new file mode 100644 index 00000000000..18eb5190ce4 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/initvals.c @@ -0,0 +1,1555 @@ +/* + * Initial register settings functions + * + * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com> + * + * Permission to use, copy, modify, and 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 "ath5k.h" +#include "reg.h" +#include "debug.h" +#include "base.h" + +/* + * Mode-independent initial register writes + */ + +struct ath5k_ini { + u16 ini_register; + u32 ini_value; + + enum { + AR5K_INI_WRITE = 0, /* Default */ + AR5K_INI_READ = 1, /* Cleared on read */ + } ini_mode; +}; + +/* + * Mode specific initial register values + */ + +struct ath5k_ini_mode { + u16 mode_register; + u32 mode_value[5]; +}; + +/* Initial register settings for AR5210 */ +static const struct ath5k_ini ar5210_ini[] = { + /* PCU and MAC registers */ + { AR5K_NOQCU_TXDP0, 0 }, + { AR5K_NOQCU_TXDP1, 0 }, + { AR5K_RXDP, 0 }, + { AR5K_CR, 0 }, + { AR5K_ISR, 0, AR5K_INI_READ }, + { AR5K_IMR, 0 }, + { AR5K_IER, AR5K_IER_DISABLE }, + { AR5K_BSR, 0, AR5K_INI_READ }, + { AR5K_TXCFG, AR5K_DMASIZE_128B }, + { AR5K_RXCFG, AR5K_DMASIZE_128B }, + { AR5K_CFG, AR5K_INIT_CFG }, + { AR5K_TOPS, 8 }, + { AR5K_RXNOFRM, 8 }, + { AR5K_RPGTO, 0 }, + { AR5K_TXNOFRM, 0 }, + { AR5K_SFR, 0 }, + { AR5K_MIBC, 0 }, + { AR5K_MISC, 0 }, + { AR5K_RX_FILTER_5210, 0 }, + { AR5K_MCAST_FILTER0_5210, 0 }, + { AR5K_MCAST_FILTER1_5210, 0 }, + { AR5K_TX_MASK0, 0 }, + { AR5K_TX_MASK1, 0 }, + { AR5K_CLR_TMASK, 0 }, + { AR5K_TRIG_LVL, AR5K_TUNE_MIN_TX_FIFO_THRES }, + { AR5K_DIAG_SW_5210, 0 }, + { AR5K_RSSI_THR, AR5K_TUNE_RSSI_THRES }, + { AR5K_TSF_L32_5210, 0 }, + { AR5K_TIMER0_5210, 0 }, + { AR5K_TIMER1_5210, 0xffffffff }, + { AR5K_TIMER2_5210, 0xffffffff }, + { AR5K_TIMER3_5210, 1 }, + { AR5K_CFP_DUR_5210, 0 }, + { AR5K_CFP_PERIOD_5210, 0 }, + /* PHY registers */ + { AR5K_PHY(0), 0x00000047 }, + { AR5K_PHY_AGC, 0x00000000 }, + { AR5K_PHY(3), 0x09848ea6 }, + { AR5K_PHY(4), 0x3d32e000 }, + { AR5K_PHY(5), 0x0000076b }, + { AR5K_PHY_ACT, AR5K_PHY_ACT_DISABLE }, + { AR5K_PHY(8), 0x02020200 }, + { AR5K_PHY(9), 0x00000e0e }, + { AR5K_PHY(10), 0x0a020201 }, + { AR5K_PHY(11), 0x00036ffc }, + { AR5K_PHY(12), 0x00000000 }, + { AR5K_PHY(13), 0x00000e0e }, + { AR5K_PHY(14), 0x00000007 }, + { AR5K_PHY(15), 0x00020100 }, + { AR5K_PHY(16), 0x89630000 }, + { AR5K_PHY(17), 0x1372169c }, + { AR5K_PHY(18), 0x0018b633 }, + { AR5K_PHY(19), 0x1284613c }, + { AR5K_PHY(20), 0x0de8b8e0 }, + { AR5K_PHY(21), 0x00074859 }, + { AR5K_PHY(22), 0x7e80beba }, + { AR5K_PHY(23), 0x313a665e }, + { AR5K_PHY_AGCCTL, 0x00001d08 }, + { AR5K_PHY(25), 0x0001ce00 }, + { AR5K_PHY(26), 0x409a4190 }, + { AR5K_PHY(28), 0x0000000f }, + { AR5K_PHY(29), 0x00000080 }, + { AR5K_PHY(30), 0x00000004 }, + { AR5K_PHY(31), 0x00000018 }, /* 0x987c */ + { AR5K_PHY(64), 0x00000000 }, /* 0x9900 */ + { AR5K_PHY(65), 0x00000000 }, + { AR5K_PHY(66), 0x00000000 }, + { AR5K_PHY(67), 0x00800000 }, + { AR5K_PHY(68), 0x00000003 }, + /* BB gain table (64bytes) */ + { AR5K_BB_GAIN(0), 0x00000000 }, + { AR5K_BB_GAIN(1), 0x00000020 }, + { AR5K_BB_GAIN(2), 0x00000010 }, + { AR5K_BB_GAIN(3), 0x00000030 }, + { AR5K_BB_GAIN(4), 0x00000008 }, + { AR5K_BB_GAIN(5), 0x00000028 }, + { AR5K_BB_GAIN(6), 0x00000028 }, + { AR5K_BB_GAIN(7), 0x00000004 }, + { AR5K_BB_GAIN(8), 0x00000024 }, + { AR5K_BB_GAIN(9), 0x00000014 }, + { AR5K_BB_GAIN(10), 0x00000034 }, + { AR5K_BB_GAIN(11), 0x0000000c }, + { AR5K_BB_GAIN(12), 0x0000002c }, + { AR5K_BB_GAIN(13), 0x00000002 }, + { AR5K_BB_GAIN(14), 0x00000022 }, + { AR5K_BB_GAIN(15), 0x00000012 }, + { AR5K_BB_GAIN(16), 0x00000032 }, + { AR5K_BB_GAIN(17), 0x0000000a }, + { AR5K_BB_GAIN(18), 0x0000002a }, + { AR5K_BB_GAIN(19), 0x00000001 }, + { AR5K_BB_GAIN(20), 0x00000021 }, + { AR5K_BB_GAIN(21), 0x00000011 }, + { AR5K_BB_GAIN(22), 0x00000031 }, + { AR5K_BB_GAIN(23), 0x00000009 }, + { AR5K_BB_GAIN(24), 0x00000029 }, + { AR5K_BB_GAIN(25), 0x00000005 }, + { AR5K_BB_GAIN(26), 0x00000025 }, + { AR5K_BB_GAIN(27), 0x00000015 }, + { AR5K_BB_GAIN(28), 0x00000035 }, + { AR5K_BB_GAIN(29), 0x0000000d }, + { AR5K_BB_GAIN(30), 0x0000002d }, + { AR5K_BB_GAIN(31), 0x00000003 }, + { AR5K_BB_GAIN(32), 0x00000023 }, + { AR5K_BB_GAIN(33), 0x00000013 }, + { AR5K_BB_GAIN(34), 0x00000033 }, + { AR5K_BB_GAIN(35), 0x0000000b }, + { AR5K_BB_GAIN(36), 0x0000002b }, + { AR5K_BB_GAIN(37), 0x00000007 }, + { AR5K_BB_GAIN(38), 0x00000027 }, + { AR5K_BB_GAIN(39), 0x00000017 }, + { AR5K_BB_GAIN(40), 0x00000037 }, + { AR5K_BB_GAIN(41), 0x0000000f }, + { AR5K_BB_GAIN(42), 0x0000002f }, + { AR5K_BB_GAIN(43), 0x0000002f }, + { AR5K_BB_GAIN(44), 0x0000002f }, + { AR5K_BB_GAIN(45), 0x0000002f }, + { AR5K_BB_GAIN(46), 0x0000002f }, + { AR5K_BB_GAIN(47), 0x0000002f }, + { AR5K_BB_GAIN(48), 0x0000002f }, + { AR5K_BB_GAIN(49), 0x0000002f }, + { AR5K_BB_GAIN(50), 0x0000002f }, + { AR5K_BB_GAIN(51), 0x0000002f }, + { AR5K_BB_GAIN(52), 0x0000002f }, + { AR5K_BB_GAIN(53), 0x0000002f }, + { AR5K_BB_GAIN(54), 0x0000002f }, + { AR5K_BB_GAIN(55), 0x0000002f }, + { AR5K_BB_GAIN(56), 0x0000002f }, + { AR5K_BB_GAIN(57), 0x0000002f }, + { AR5K_BB_GAIN(58), 0x0000002f }, + { AR5K_BB_GAIN(59), 0x0000002f }, + { AR5K_BB_GAIN(60), 0x0000002f }, + { AR5K_BB_GAIN(61), 0x0000002f }, + { AR5K_BB_GAIN(62), 0x0000002f }, + { AR5K_BB_GAIN(63), 0x0000002f }, + /* 5110 RF gain table (64btes) */ + { AR5K_RF_GAIN(0), 0x0000001d }, + { AR5K_RF_GAIN(1), 0x0000005d }, + { AR5K_RF_GAIN(2), 0x0000009d }, + { AR5K_RF_GAIN(3), 0x000000dd }, + { AR5K_RF_GAIN(4), 0x0000011d }, + { AR5K_RF_GAIN(5), 0x00000021 }, + { AR5K_RF_GAIN(6), 0x00000061 }, + { AR5K_RF_GAIN(7), 0x000000a1 }, + { AR5K_RF_GAIN(8), 0x000000e1 }, + { AR5K_RF_GAIN(9), 0x00000031 }, + { AR5K_RF_GAIN(10), 0x00000071 }, + { AR5K_RF_GAIN(11), 0x000000b1 }, + { AR5K_RF_GAIN(12), 0x0000001c }, + { AR5K_RF_GAIN(13), 0x0000005c }, + { AR5K_RF_GAIN(14), 0x00000029 }, + { AR5K_RF_GAIN(15), 0x00000069 }, + { AR5K_RF_GAIN(16), 0x000000a9 }, + { AR5K_RF_GAIN(17), 0x00000020 }, + { AR5K_RF_GAIN(18), 0x00000019 }, + { AR5K_RF_GAIN(19), 0x00000059 }, + { AR5K_RF_GAIN(20), 0x00000099 }, + { AR5K_RF_GAIN(21), 0x00000030 }, + { AR5K_RF_GAIN(22), 0x00000005 }, + { AR5K_RF_GAIN(23), 0x00000025 }, + { AR5K_RF_GAIN(24), 0x00000065 }, + { AR5K_RF_GAIN(25), 0x000000a5 }, + { AR5K_RF_GAIN(26), 0x00000028 }, + { AR5K_RF_GAIN(27), 0x00000068 }, + { AR5K_RF_GAIN(28), 0x0000001f }, + { AR5K_RF_GAIN(29), 0x0000001e }, + { AR5K_RF_GAIN(30), 0x00000018 }, + { AR5K_RF_GAIN(31), 0x00000058 }, + { AR5K_RF_GAIN(32), 0x00000098 }, + { AR5K_RF_GAIN(33), 0x00000003 }, + { AR5K_RF_GAIN(34), 0x00000004 }, + { AR5K_RF_GAIN(35), 0x00000044 }, + { AR5K_RF_GAIN(36), 0x00000084 }, + { AR5K_RF_GAIN(37), 0x00000013 }, + { AR5K_RF_GAIN(38), 0x00000012 }, + { AR5K_RF_GAIN(39), 0x00000052 }, + { AR5K_RF_GAIN(40), 0x00000092 }, + { AR5K_RF_GAIN(41), 0x000000d2 }, + { AR5K_RF_GAIN(42), 0x0000002b }, + { AR5K_RF_GAIN(43), 0x0000002a }, + { AR5K_RF_GAIN(44), 0x0000006a }, + { AR5K_RF_GAIN(45), 0x000000aa }, + { AR5K_RF_GAIN(46), 0x0000001b }, + { AR5K_RF_GAIN(47), 0x0000001a }, + { AR5K_RF_GAIN(48), 0x0000005a }, + { AR5K_RF_GAIN(49), 0x0000009a }, + { AR5K_RF_GAIN(50), 0x000000da }, + { AR5K_RF_GAIN(51), 0x00000006 }, + { AR5K_RF_GAIN(52), 0x00000006 }, + { AR5K_RF_GAIN(53), 0x00000006 }, + { AR5K_RF_GAIN(54), 0x00000006 }, + { AR5K_RF_GAIN(55), 0x00000006 }, + { AR5K_RF_GAIN(56), 0x00000006 }, + { AR5K_RF_GAIN(57), 0x00000006 }, + { AR5K_RF_GAIN(58), 0x00000006 }, + { AR5K_RF_GAIN(59), 0x00000006 }, + { AR5K_RF_GAIN(60), 0x00000006 }, + { AR5K_RF_GAIN(61), 0x00000006 }, + { AR5K_RF_GAIN(62), 0x00000006 }, + { AR5K_RF_GAIN(63), 0x00000006 }, + /* PHY activation */ + { AR5K_PHY(53), 0x00000020 }, + { AR5K_PHY(51), 0x00000004 }, + { AR5K_PHY(50), 0x00060106 }, + { AR5K_PHY(39), 0x0000006d }, + { AR5K_PHY(48), 0x00000000 }, + { AR5K_PHY(52), 0x00000014 }, + { AR5K_PHY_ACT, AR5K_PHY_ACT_ENABLE }, +}; + +/* Initial register settings for AR5211 */ +static const struct ath5k_ini ar5211_ini[] = { + { AR5K_RXDP, 0x00000000 }, + { AR5K_RTSD0, 0x84849c9c }, + { AR5K_RTSD1, 0x7c7c7c7c }, + { AR5K_RXCFG, 0x00000005 }, + { AR5K_MIBC, 0x00000000 }, + { AR5K_TOPS, 0x00000008 }, + { AR5K_RXNOFRM, 0x00000008 }, + { AR5K_TXNOFRM, 0x00000010 }, + { AR5K_RPGTO, 0x00000000 }, + { AR5K_RFCNT, 0x0000001f }, + { AR5K_QUEUE_TXDP(0), 0x00000000 }, + { AR5K_QUEUE_TXDP(1), 0x00000000 }, + { AR5K_QUEUE_TXDP(2), 0x00000000 }, + { AR5K_QUEUE_TXDP(3), 0x00000000 }, + { AR5K_QUEUE_TXDP(4), 0x00000000 }, + { AR5K_QUEUE_TXDP(5), 0x00000000 }, + { AR5K_QUEUE_TXDP(6), 0x00000000 }, + { AR5K_QUEUE_TXDP(7), 0x00000000 }, + { AR5K_QUEUE_TXDP(8), 0x00000000 }, + { AR5K_QUEUE_TXDP(9), 0x00000000 }, + { AR5K_DCU_FP, 0x00000000 }, + { AR5K_STA_ID1, 0x00000000 }, + { AR5K_BSS_ID0, 0x00000000 }, + { AR5K_BSS_ID1, 0x00000000 }, + { AR5K_RSSI_THR, 0x00000000 }, + { AR5K_CFP_PERIOD_5211, 0x00000000 }, + { AR5K_TIMER0_5211, 0x00000030 }, + { AR5K_TIMER1_5211, 0x0007ffff }, + { AR5K_TIMER2_5211, 0x01ffffff }, + { AR5K_TIMER3_5211, 0x00000031 }, + { AR5K_CFP_DUR_5211, 0x00000000 }, + { AR5K_RX_FILTER_5211, 0x00000000 }, + { AR5K_MCAST_FILTER0_5211, 0x00000000 }, + { AR5K_MCAST_FILTER1_5211, 0x00000002 }, + { AR5K_DIAG_SW_5211, 0x00000000 }, + { AR5K_ADDAC_TEST, 0x00000000 }, + { AR5K_DEFAULT_ANTENNA, 0x00000000 }, + /* PHY registers */ + { AR5K_PHY_AGC, 0x00000000 }, + { AR5K_PHY(3), 0x2d849093 }, + { AR5K_PHY(4), 0x7d32e000 }, + { AR5K_PHY(5), 0x00000f6b }, + { AR5K_PHY_ACT, 0x00000000 }, + { AR5K_PHY(11), 0x00026ffe }, + { AR5K_PHY(12), 0x00000000 }, + { AR5K_PHY(15), 0x00020100 }, + { AR5K_PHY(16), 0x206a017a }, + { AR5K_PHY(19), 0x1284613c }, + { AR5K_PHY(21), 0x00000859 }, + { AR5K_PHY(26), 0x409a4190 }, /* 0x9868 */ + { AR5K_PHY(27), 0x050cb081 }, + { AR5K_PHY(28), 0x0000000f }, + { AR5K_PHY(29), 0x00000080 }, + { AR5K_PHY(30), 0x0000000c }, + { AR5K_PHY(64), 0x00000000 }, + { AR5K_PHY(65), 0x00000000 }, + { AR5K_PHY(66), 0x00000000 }, + { AR5K_PHY(67), 0x00800000 }, + { AR5K_PHY(68), 0x00000001 }, + { AR5K_PHY(71), 0x0000092a }, + { AR5K_PHY_IQ, 0x00000000 }, + { AR5K_PHY(73), 0x00058a05 }, + { AR5K_PHY(74), 0x00000001 }, + { AR5K_PHY(75), 0x00000000 }, + { AR5K_PHY_PAPD_PROBE, 0x00000000 }, + { AR5K_PHY(77), 0x00000000 }, /* 0x9934 */ + { AR5K_PHY(78), 0x00000000 }, /* 0x9938 */ + { AR5K_PHY(79), 0x0000003f }, /* 0x993c */ + { AR5K_PHY(80), 0x00000004 }, + { AR5K_PHY(82), 0x00000000 }, + { AR5K_PHY(83), 0x00000000 }, + { AR5K_PHY(84), 0x00000000 }, + { AR5K_PHY_RADAR, 0x5d50f14c }, + { AR5K_PHY(86), 0x00000018 }, + { AR5K_PHY(87), 0x004b6a8e }, + /* Initial Power table (32bytes) + * common on all cards/modes. + * Note: Table is rewritten during + * txpower setup later using calibration + * data etc. so next write is non-common */ + { AR5K_PHY_PCDAC_TXPOWER(1), 0x06ff05ff }, + { AR5K_PHY_PCDAC_TXPOWER(2), 0x07ff07ff }, + { AR5K_PHY_PCDAC_TXPOWER(3), 0x08ff08ff }, + { AR5K_PHY_PCDAC_TXPOWER(4), 0x09ff09ff }, + { AR5K_PHY_PCDAC_TXPOWER(5), 0x0aff0aff }, + { AR5K_PHY_PCDAC_TXPOWER(6), 0x0bff0bff }, + { AR5K_PHY_PCDAC_TXPOWER(7), 0x0cff0cff }, + { AR5K_PHY_PCDAC_TXPOWER(8), 0x0dff0dff }, + { AR5K_PHY_PCDAC_TXPOWER(9), 0x0fff0eff }, + { AR5K_PHY_PCDAC_TXPOWER(10), 0x12ff12ff }, + { AR5K_PHY_PCDAC_TXPOWER(11), 0x14ff13ff }, + { AR5K_PHY_PCDAC_TXPOWER(12), 0x16ff15ff }, + { AR5K_PHY_PCDAC_TXPOWER(13), 0x19ff17ff }, + { AR5K_PHY_PCDAC_TXPOWER(14), 0x1bff1aff }, + { AR5K_PHY_PCDAC_TXPOWER(15), 0x1eff1dff }, + { AR5K_PHY_PCDAC_TXPOWER(16), 0x23ff20ff }, + { AR5K_PHY_PCDAC_TXPOWER(17), 0x27ff25ff }, + { AR5K_PHY_PCDAC_TXPOWER(18), 0x2cff29ff }, + { AR5K_PHY_PCDAC_TXPOWER(19), 0x31ff2fff }, + { AR5K_PHY_PCDAC_TXPOWER(20), 0x37ff34ff }, + { AR5K_PHY_PCDAC_TXPOWER(21), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(22), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(23), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(24), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(25), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(26), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(27), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(28), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(29), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(30), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(31), 0x3aff3aff }, + { AR5K_PHY_CCKTXCTL, 0x00000000 }, + { AR5K_PHY(642), 0x503e4646 }, + { AR5K_PHY_GAIN_2GHZ, 0x6480416c }, + { AR5K_PHY(644), 0x0199a003 }, + { AR5K_PHY(645), 0x044cd610 }, + { AR5K_PHY(646), 0x13800040 }, + { AR5K_PHY(647), 0x1be00060 }, + { AR5K_PHY(648), 0x0c53800a }, + { AR5K_PHY(649), 0x0014df3b }, + { AR5K_PHY(650), 0x000001b5 }, + { AR5K_PHY(651), 0x00000020 }, +}; + +/* Initial mode-specific settings for AR5211 + * 5211 supports OFDM-only g (draft g) but we + * need to test it ! + */ +static const struct ath5k_ini_mode ar5211_ini_mode[] = { + { AR5K_TXCFG, + /* a aTurbo b g (OFDM) */ + { 0x00000015, 0x00000015, 0x0000001d, 0x00000015 } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(0), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(1), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(2), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(3), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(4), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(5), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(6), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(7), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(8), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(9), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, + { AR5K_DCU_GBL_IFS_SLOT, + { 0x00000168, 0x000001e0, 0x000001b8, 0x00000168 } }, + { AR5K_DCU_GBL_IFS_SIFS, + { 0x00000230, 0x000001e0, 0x000000b0, 0x00000230 } }, + { AR5K_DCU_GBL_IFS_EIFS, + { 0x00000d98, 0x00001180, 0x00001f48, 0x00000d98 } }, + { AR5K_DCU_GBL_IFS_MISC, + { 0x0000a0e0, 0x00014068, 0x00005880, 0x0000a0e0 } }, + { AR5K_TIME_OUT, + { 0x04000400, 0x08000800, 0x20003000, 0x04000400 } }, + { AR5K_USEC_5211, + { 0x0e8d8fa7, 0x0e8d8fcf, 0x01608f95, 0x0e8d8fa7 } }, + { AR5K_PHY_TURBO, + { 0x00000000, 0x00000003, 0x00000000, 0x00000000 } }, + { AR5K_PHY(8), + { 0x02020200, 0x02020200, 0x02010200, 0x02020200 } }, + { AR5K_PHY(9), + { 0x00000e0e, 0x00000e0e, 0x00000707, 0x00000e0e } }, + { AR5K_PHY(10), + { 0x0a020001, 0x0a020001, 0x05010000, 0x0a020001 } }, + { AR5K_PHY(13), + { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, + { AR5K_PHY(14), + { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b } }, + { AR5K_PHY(17), + { 0x1372169c, 0x137216a5, 0x137216a8, 0x1372169c } }, + { AR5K_PHY(18), + { 0x0018ba67, 0x0018ba67, 0x0018ba69, 0x0018ba69 } }, + { AR5K_PHY(20), + { 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0 } }, + { AR5K_PHY_SIG, + { 0x7e800d2e, 0x7e800d2e, 0x7ec00d2e, 0x7e800d2e } }, + { AR5K_PHY_AGCCOARSE, + { 0x31375d5e, 0x31375d5e, 0x313a5d5e, 0x31375d5e } }, + { AR5K_PHY_AGCCTL, + { 0x0000bd10, 0x0000bd10, 0x0000bd38, 0x0000bd10 } }, + { AR5K_PHY_NF, + { 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } }, + { AR5K_PHY_RX_DELAY, + { 0x00002710, 0x00002710, 0x0000157c, 0x00002710 } }, + { AR5K_PHY(70), + { 0x00000190, 0x00000190, 0x00000084, 0x00000190 } }, + { AR5K_PHY_FRAME_CTL_5211, + { 0x6fe01020, 0x6fe01020, 0x6fe00920, 0x6fe01020 } }, + { AR5K_PHY_PCDAC_TXPOWER_BASE, + { 0x05ff14ff, 0x05ff14ff, 0x05ff14ff, 0x05ff19ff } }, + { AR5K_RF_BUFFER_CONTROL_4, + { 0x00000010, 0x00000014, 0x00000010, 0x00000010 } }, +}; + +/* Initial register settings for AR5212 */ +static const struct ath5k_ini ar5212_ini_common_start[] = { + { AR5K_RXDP, 0x00000000 }, + { AR5K_RXCFG, 0x00000005 }, + { AR5K_MIBC, 0x00000000 }, + { AR5K_TOPS, 0x00000008 }, + { AR5K_RXNOFRM, 0x00000008 }, + { AR5K_TXNOFRM, 0x00000010 }, + { AR5K_RPGTO, 0x00000000 }, + { AR5K_RFCNT, 0x0000001f }, + { AR5K_QUEUE_TXDP(0), 0x00000000 }, + { AR5K_QUEUE_TXDP(1), 0x00000000 }, + { AR5K_QUEUE_TXDP(2), 0x00000000 }, + { AR5K_QUEUE_TXDP(3), 0x00000000 }, + { AR5K_QUEUE_TXDP(4), 0x00000000 }, + { AR5K_QUEUE_TXDP(5), 0x00000000 }, + { AR5K_QUEUE_TXDP(6), 0x00000000 }, + { AR5K_QUEUE_TXDP(7), 0x00000000 }, + { AR5K_QUEUE_TXDP(8), 0x00000000 }, + { AR5K_QUEUE_TXDP(9), 0x00000000 }, + { AR5K_DCU_FP, 0x00000000 }, + { AR5K_DCU_TXP, 0x00000000 }, + /* Tx filter table 0 (32 entries) */ + { AR5K_DCU_TX_FILTER_0(0), 0x00000000 }, /* DCU 0 */ + { AR5K_DCU_TX_FILTER_0(1), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(2), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(3), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(4), 0x00000000 }, /* DCU 1 */ + { AR5K_DCU_TX_FILTER_0(5), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(6), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(7), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(8), 0x00000000 }, /* DCU 2 */ + { AR5K_DCU_TX_FILTER_0(9), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(10), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(11), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(12), 0x00000000 }, /* DCU 3 */ + { AR5K_DCU_TX_FILTER_0(13), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(14), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(15), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(16), 0x00000000 }, /* DCU 4 */ + { AR5K_DCU_TX_FILTER_0(17), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(18), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(19), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(20), 0x00000000 }, /* DCU 5 */ + { AR5K_DCU_TX_FILTER_0(21), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(22), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(23), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(24), 0x00000000 }, /* DCU 6 */ + { AR5K_DCU_TX_FILTER_0(25), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(26), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(27), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(28), 0x00000000 }, /* DCU 7 */ + { AR5K_DCU_TX_FILTER_0(29), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(30), 0x00000000 }, + { AR5K_DCU_TX_FILTER_0(31), 0x00000000 }, + /* Tx filter table 1 (16 entries) */ + { AR5K_DCU_TX_FILTER_1(0), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(1), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(2), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(3), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(4), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(5), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(6), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(7), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(8), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(9), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(10), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(11), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(12), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(13), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(14), 0x00000000 }, + { AR5K_DCU_TX_FILTER_1(15), 0x00000000 }, + { AR5K_DCU_TX_FILTER_CLR, 0x00000000 }, + { AR5K_DCU_TX_FILTER_SET, 0x00000000 }, + { AR5K_STA_ID1, 0x00000000 }, + { AR5K_BSS_ID0, 0x00000000 }, + { AR5K_BSS_ID1, 0x00000000 }, + { AR5K_BEACON_5211, 0x00000000 }, + { AR5K_CFP_PERIOD_5211, 0x00000000 }, + { AR5K_TIMER0_5211, 0x00000030 }, + { AR5K_TIMER1_5211, 0x0007ffff }, + { AR5K_TIMER2_5211, 0x01ffffff }, + { AR5K_TIMER3_5211, 0x00000031 }, + { AR5K_CFP_DUR_5211, 0x00000000 }, + { AR5K_RX_FILTER_5211, 0x00000000 }, + { AR5K_DIAG_SW_5211, 0x00000000 }, + { AR5K_ADDAC_TEST, 0x00000000 }, + { AR5K_DEFAULT_ANTENNA, 0x00000000 }, + { AR5K_FRAME_CTL_QOSM, 0x000fc78f }, + { AR5K_XRMODE, 0x2a82301a }, + { AR5K_XRDELAY, 0x05dc01e0 }, + { AR5K_XRTIMEOUT, 0x1f402710 }, + { AR5K_XRCHIRP, 0x01f40000 }, + { AR5K_XRSTOMP, 0x00001e1c }, + { AR5K_SLEEP0, 0x0002aaaa }, + { AR5K_SLEEP1, 0x02005555 }, + { AR5K_SLEEP2, 0x00000000 }, + { AR5K_BSS_IDM0, 0xffffffff }, + { AR5K_BSS_IDM1, 0x0000ffff }, + { AR5K_TXPC, 0x00000000 }, + { AR5K_PROFCNT_TX, 0x00000000 }, + { AR5K_PROFCNT_RX, 0x00000000 }, + { AR5K_PROFCNT_RXCLR, 0x00000000 }, + { AR5K_PROFCNT_CYCLE, 0x00000000 }, + { AR5K_QUIET_CTL1, 0x00000088 }, + /* Initial rate duration table (32 entries )*/ + { AR5K_RATE_DUR(0), 0x00000000 }, + { AR5K_RATE_DUR(1), 0x0000008c }, + { AR5K_RATE_DUR(2), 0x000000e4 }, + { AR5K_RATE_DUR(3), 0x000002d5 }, + { AR5K_RATE_DUR(4), 0x00000000 }, + { AR5K_RATE_DUR(5), 0x00000000 }, + { AR5K_RATE_DUR(6), 0x000000a0 }, + { AR5K_RATE_DUR(7), 0x000001c9 }, + { AR5K_RATE_DUR(8), 0x0000002c }, + { AR5K_RATE_DUR(9), 0x0000002c }, + { AR5K_RATE_DUR(10), 0x00000030 }, + { AR5K_RATE_DUR(11), 0x0000003c }, + { AR5K_RATE_DUR(12), 0x0000002c }, + { AR5K_RATE_DUR(13), 0x0000002c }, + { AR5K_RATE_DUR(14), 0x00000030 }, + { AR5K_RATE_DUR(15), 0x0000003c }, + { AR5K_RATE_DUR(16), 0x00000000 }, + { AR5K_RATE_DUR(17), 0x00000000 }, + { AR5K_RATE_DUR(18), 0x00000000 }, + { AR5K_RATE_DUR(19), 0x00000000 }, + { AR5K_RATE_DUR(20), 0x00000000 }, + { AR5K_RATE_DUR(21), 0x00000000 }, + { AR5K_RATE_DUR(22), 0x00000000 }, + { AR5K_RATE_DUR(23), 0x00000000 }, + { AR5K_RATE_DUR(24), 0x000000d5 }, + { AR5K_RATE_DUR(25), 0x000000df }, + { AR5K_RATE_DUR(26), 0x00000102 }, + { AR5K_RATE_DUR(27), 0x0000013a }, + { AR5K_RATE_DUR(28), 0x00000075 }, + { AR5K_RATE_DUR(29), 0x0000007f }, + { AR5K_RATE_DUR(30), 0x000000a2 }, + { AR5K_RATE_DUR(31), 0x00000000 }, + { AR5K_QUIET_CTL2, 0x00010002 }, + { AR5K_TSF_PARM, 0x00000001 }, + { AR5K_QOS_NOACK, 0x000000c0 }, + { AR5K_PHY_ERR_FIL, 0x00000000 }, + { AR5K_XRLAT_TX, 0x00000168 }, + { AR5K_ACKSIFS, 0x00000000 }, + /* Rate -> db table + * notice ...03<-02<-01<-00 ! */ + { AR5K_RATE2DB(0), 0x03020100 }, + { AR5K_RATE2DB(1), 0x07060504 }, + { AR5K_RATE2DB(2), 0x0b0a0908 }, + { AR5K_RATE2DB(3), 0x0f0e0d0c }, + { AR5K_RATE2DB(4), 0x13121110 }, + { AR5K_RATE2DB(5), 0x17161514 }, + { AR5K_RATE2DB(6), 0x1b1a1918 }, + { AR5K_RATE2DB(7), 0x1f1e1d1c }, + /* Db -> Rate table */ + { AR5K_DB2RATE(0), 0x03020100 }, + { AR5K_DB2RATE(1), 0x07060504 }, + { AR5K_DB2RATE(2), 0x0b0a0908 }, + { AR5K_DB2RATE(3), 0x0f0e0d0c }, + { AR5K_DB2RATE(4), 0x13121110 }, + { AR5K_DB2RATE(5), 0x17161514 }, + { AR5K_DB2RATE(6), 0x1b1a1918 }, + { AR5K_DB2RATE(7), 0x1f1e1d1c }, + /* PHY registers (Common settings + * for all chips/modes) */ + { AR5K_PHY(3), 0xad848e19 }, + { AR5K_PHY(4), 0x7d28e000 }, + { AR5K_PHY_TIMING_3, 0x9c0a9f6b }, + { AR5K_PHY_ACT, 0x00000000 }, + { AR5K_PHY(16), 0x206a017a }, + { AR5K_PHY(21), 0x00000859 }, + { AR5K_PHY_BIN_MASK_1, 0x00000000 }, + { AR5K_PHY_BIN_MASK_2, 0x00000000 }, + { AR5K_PHY_BIN_MASK_3, 0x00000000 }, + { AR5K_PHY_BIN_MASK_CTL, 0x00800000 }, + { AR5K_PHY_ANT_CTL, 0x00000001 }, + /*{ AR5K_PHY(71), 0x0000092a },*/ /* Old value */ + { AR5K_PHY_MAX_RX_LEN, 0x00000c80 }, + { AR5K_PHY_IQ, 0x05100000 }, + { AR5K_PHY_WARM_RESET, 0x00000001 }, + { AR5K_PHY_CTL, 0x00000004 }, + { AR5K_PHY_TXPOWER_RATE1, 0x1e1f2022 }, + { AR5K_PHY_TXPOWER_RATE2, 0x0a0b0c0d }, + { AR5K_PHY_TXPOWER_RATE_MAX, 0x0000003f }, + { AR5K_PHY(82), 0x9280b212 }, + { AR5K_PHY_RADAR, 0x5d50e188 }, + /*{ AR5K_PHY(86), 0x000000ff },*/ + { AR5K_PHY(87), 0x004b6a8e }, + { AR5K_PHY_NFTHRES, 0x000003ce }, + { AR5K_PHY_RESTART, 0x192fb515 }, + { AR5K_PHY(94), 0x00000001 }, + { AR5K_PHY_RFBUS_REQ, 0x00000000 }, + /*{ AR5K_PHY(644), 0x0080a333 },*/ /* Old value */ + /*{ AR5K_PHY(645), 0x00206c10 },*/ /* Old value */ + { AR5K_PHY(644), 0x00806333 }, + { AR5K_PHY(645), 0x00106c10 }, + { AR5K_PHY(646), 0x009c4060 }, + /* { AR5K_PHY(647), 0x1483800a }, */ + /* { AR5K_PHY(648), 0x01831061 }, */ /* Old value */ + { AR5K_PHY(648), 0x018830c6 }, + { AR5K_PHY(649), 0x00000400 }, + /*{ AR5K_PHY(650), 0x000001b5 },*/ + { AR5K_PHY(651), 0x00000000 }, + { AR5K_PHY_TXPOWER_RATE3, 0x20202020 }, + { AR5K_PHY_TXPOWER_RATE4, 0x20202020 }, + /*{ AR5K_PHY(655), 0x13c889af },*/ + { AR5K_PHY(656), 0x38490a20 }, + { AR5K_PHY(657), 0x00007bb6 }, + { AR5K_PHY(658), 0x0fff3ffc }, +}; + +/* Initial mode-specific settings for AR5212 (Written before ar5212_ini) */ +static const struct ath5k_ini_mode ar5212_ini_mode_start[] = { + { AR5K_QUEUE_DFS_LOCAL_IFS(0), + /* a/XR aTurbo b g (DYN) gTurbo */ + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(1), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(2), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(3), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(4), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(5), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(6), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(7), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(8), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(9), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_DCU_GBL_IFS_SIFS, + { 0x00000230, 0x000001e0, 0x000000b0, 0x00000160, 0x000001e0 } }, + { AR5K_DCU_GBL_IFS_SLOT, + { 0x00000168, 0x000001e0, 0x000001b8, 0x0000018c, 0x000001e0 } }, + { AR5K_DCU_GBL_IFS_EIFS, + { 0x00000e60, 0x00001180, 0x00001f1c, 0x00003e38, 0x00001180 } }, + { AR5K_DCU_GBL_IFS_MISC, + { 0x0000a0e0, 0x00014068, 0x00005880, 0x0000b0e0, 0x00014068 } }, + { AR5K_TIME_OUT, + { 0x03e803e8, 0x06e006e0, 0x04200420, 0x08400840, 0x06e006e0 } }, + { AR5K_PHY_TURBO, + { 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000003 } }, + { AR5K_PHY(8), + { 0x02020200, 0x02020200, 0x02010200, 0x02020200, 0x02020200 } }, + { AR5K_PHY_RF_CTL2, + { 0x00000e0e, 0x00000e0e, 0x00000707, 0x00000e0e, 0x00000e0e } }, + { AR5K_PHY_SETTLING, + { 0x1372161c, 0x13721c25, 0x13721722, 0x137216a2, 0x13721c25 } }, + { AR5K_PHY_AGCCTL, + { 0x00009d10, 0x00009d10, 0x00009d18, 0x00009d18, 0x00009d10 } }, + { AR5K_PHY_NF, + { 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } }, + { AR5K_PHY_WEAK_OFDM_HIGH_THR, + { 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 } }, + { AR5K_PHY(70), + { 0x000001b8, 0x000001b8, 0x00000084, 0x00000108, 0x000001b8 } }, + { AR5K_PHY_OFDM_SELFCORR, + { 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05 } }, + { 0xa230, + { 0x00000000, 0x00000000, 0x00000000, 0x00000108, 0x00000000 } }, +}; + +/* Initial mode-specific settings for AR5212 + RF5111 (Written after ar5212_ini) */ +static const struct ath5k_ini_mode rf5111_ini_mode_end[] = { + { AR5K_TXCFG, + /* a/XR aTurbo b g (DYN) gTurbo */ + { 0x00008015, 0x00008015, 0x00008015, 0x00008015, 0x00008015 } }, + { AR5K_USEC_5211, + { 0x128d8fa7, 0x09880fcf, 0x04e00f95, 0x12e00fab, 0x09880fcf } }, + { AR5K_PHY_RF_CTL3, + { 0x0a020001, 0x0a020001, 0x05010100, 0x0a020001, 0x0a020001 } }, + { AR5K_PHY_RF_CTL4, + { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, + { AR5K_PHY_PA_CTL, + { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } }, + { AR5K_PHY_GAIN, + { 0x0018da5a, 0x0018da5a, 0x0018ca69, 0x0018ca69, 0x0018ca69 } }, + { AR5K_PHY_DESIRED_SIZE, + { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } }, + { AR5K_PHY_SIG, + { 0x7e800d2e, 0x7e800d2e, 0x7ee84d2e, 0x7ee84d2e, 0x7e800d2e } }, + { AR5K_PHY_AGCCOARSE, + { 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e, 0x3137615e } }, + { AR5K_PHY_WEAK_OFDM_LOW_THR, + { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb080, 0x050cb080 } }, + { AR5K_PHY_RX_DELAY, + { 0x00002710, 0x00002710, 0x0000157c, 0x00002af8, 0x00002710 } }, + { AR5K_PHY_FRAME_CTL_5211, + { 0xf7b81020, 0xf7b81020, 0xf7b80d20, 0xf7b81020, 0xf7b81020 } }, + { AR5K_PHY_GAIN_2GHZ, + { 0x642c416a, 0x642c416a, 0x6440416a, 0x6440416a, 0x6440416a } }, + { AR5K_PHY_CCK_RX_CTL_4, + { 0x1883800a, 0x1883800a, 0x1873800a, 0x1883800a, 0x1883800a } }, +}; + +static const struct ath5k_ini rf5111_ini_common_end[] = { + { AR5K_DCU_FP, 0x00000000 }, + { AR5K_PHY_AGC, 0x00000000 }, + { AR5K_PHY_ADC_CTL, 0x00022ffe }, + { 0x983c, 0x00020100 }, + { AR5K_PHY_GAIN_OFFSET, 0x1284613c }, + { AR5K_PHY_PAPD_PROBE, 0x00004883 }, + { 0x9940, 0x00000004 }, + { 0x9958, 0x000000ff }, + { 0x9974, 0x00000000 }, + { AR5K_PHY_SPENDING, 0x00000018 }, + { AR5K_PHY_CCKTXCTL, 0x00000000 }, + { AR5K_PHY_CCK_CROSSCORR, 0xd03e6788 }, + { AR5K_PHY_DAG_CCK_CTL, 0x000001b5 }, + { 0xa23c, 0x13c889af }, +}; + +/* Initial mode-specific settings for AR5212 + RF5112 (Written after ar5212_ini) */ +static const struct ath5k_ini_mode rf5112_ini_mode_end[] = { + { AR5K_TXCFG, + /* a/XR aTurbo b g (DYN) gTurbo */ + { 0x00008015, 0x00008015, 0x00008015, 0x00008015, 0x00008015 } }, + { AR5K_USEC_5211, + { 0x128d93a7, 0x098813cf, 0x04e01395, 0x12e013ab, 0x098813cf } }, + { AR5K_PHY_RF_CTL3, + { 0x0a020001, 0x0a020001, 0x05020100, 0x0a020001, 0x0a020001 } }, + { AR5K_PHY_RF_CTL4, + { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, + { AR5K_PHY_PA_CTL, + { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } }, + { AR5K_PHY_GAIN, + { 0x0018da6d, 0x0018da6d, 0x0018ca75, 0x0018ca75, 0x0018ca75 } }, + { AR5K_PHY_DESIRED_SIZE, + { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } }, + { AR5K_PHY_SIG, + { 0x7e800d2e, 0x7e800d2e, 0x7ee80d2e, 0x7ee80d2e, 0x7e800d2e } }, + { AR5K_PHY_AGCCOARSE, + { 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e } }, + { AR5K_PHY_WEAK_OFDM_LOW_THR, + { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } }, + { AR5K_PHY_RX_DELAY, + { 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } }, + { AR5K_PHY_FRAME_CTL_5211, + { 0xf7b81020, 0xf7b81020, 0xf7b80d10, 0xf7b81010, 0xf7b81010 } }, + { AR5K_PHY_CCKTXCTL, + { 0x00000000, 0x00000000, 0x00000008, 0x00000008, 0x00000008 } }, + { AR5K_PHY_CCK_CROSSCORR, + { 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } }, + { AR5K_PHY_GAIN_2GHZ, + { 0x642c0140, 0x642c0140, 0x6442c160, 0x6442c160, 0x6442c160 } }, + { AR5K_PHY_CCK_RX_CTL_4, + { 0x1883800a, 0x1883800a, 0x1873800a, 0x1883800a, 0x1883800a } }, +}; + +static const struct ath5k_ini rf5112_ini_common_end[] = { + { AR5K_DCU_FP, 0x00000000 }, + { AR5K_PHY_AGC, 0x00000000 }, + { AR5K_PHY_ADC_CTL, 0x00022ffe }, + { 0x983c, 0x00020100 }, + { AR5K_PHY_GAIN_OFFSET, 0x1284613c }, + { AR5K_PHY_PAPD_PROBE, 0x00004882 }, + { 0x9940, 0x00000004 }, + { 0x9958, 0x000000ff }, + { 0x9974, 0x00000000 }, + { AR5K_PHY_DAG_CCK_CTL, 0x000001b5 }, + { 0xa23c, 0x13c889af }, +}; + +/* Initial mode-specific settings for RF5413/5414 (Written after ar5212_ini) */ +static const struct ath5k_ini_mode rf5413_ini_mode_end[] = { + { AR5K_TXCFG, + /* a/XR aTurbo b g (DYN) gTurbo */ + { 0x00000015, 0x00000015, 0x00000015, 0x00000015, 0x00000015 } }, + { AR5K_USEC_5211, + { 0x128d93a7, 0x098813cf, 0x04e01395, 0x12e013ab, 0x098813cf } }, + { AR5K_PHY_RF_CTL3, + { 0x0a020001, 0x0a020001, 0x05020100, 0x0a020001, 0x0a020001 } }, + { AR5K_PHY_RF_CTL4, + { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, + { AR5K_PHY_PA_CTL, + { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } }, + { AR5K_PHY_GAIN, + { 0x0018fa61, 0x0018fa61, 0x001a1a63, 0x001a1a63, 0x001a1a63 } }, + { AR5K_PHY_DESIRED_SIZE, + { 0x0c98b4e0, 0x0c98b4e0, 0x0c98b0da, 0x0c98b0da, 0x0c98b0da } }, + { AR5K_PHY_SIG, + { 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e } }, + { AR5K_PHY_AGCCOARSE, + { 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e } }, + { AR5K_PHY_WEAK_OFDM_LOW_THR, + { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } }, + { AR5K_PHY_RX_DELAY, + { 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } }, + { AR5K_PHY_FRAME_CTL_5211, + { 0xf7b81000, 0xf7b81000, 0xf7b80d00, 0xf7b81000, 0xf7b81000 } }, + { AR5K_PHY_CCKTXCTL, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { AR5K_PHY_CCK_CROSSCORR, + { 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } }, + { AR5K_PHY_GAIN_2GHZ, + { 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120 } }, + { AR5K_PHY_CCK_RX_CTL_4, + { 0x1883800a, 0x1883800a, 0x1863800a, 0x1883800a, 0x1883800a } }, + { 0xa300, + { 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000 } }, + { 0xa304, + { 0x30032602, 0x30032602, 0x30032602, 0x30032602, 0x30032602 } }, + { 0xa308, + { 0x48073e06, 0x48073e06, 0x48073e06, 0x48073e06, 0x48073e06 } }, + { 0xa30c, + { 0x560b4c0a, 0x560b4c0a, 0x560b4c0a, 0x560b4c0a, 0x560b4c0a } }, + { 0xa310, + { 0x641a600f, 0x641a600f, 0x641a600f, 0x641a600f, 0x641a600f } }, + { 0xa314, + { 0x784f6e1b, 0x784f6e1b, 0x784f6e1b, 0x784f6e1b, 0x784f6e1b } }, + { 0xa318, + { 0x868f7c5a, 0x868f7c5a, 0x868f7c5a, 0x868f7c5a, 0x868f7c5a } }, + { 0xa31c, + { 0x90cf865b, 0x90cf865b, 0x8ecf865b, 0x8ecf865b, 0x8ecf865b } }, + { 0xa320, + { 0x9d4f970f, 0x9d4f970f, 0x9b4f970f, 0x9b4f970f, 0x9b4f970f } }, + { 0xa324, + { 0xa7cfa38f, 0xa7cfa38f, 0xa3cf9f8f, 0xa3cf9f8f, 0xa3cf9f8f } }, + { 0xa328, + { 0xb55faf1f, 0xb55faf1f, 0xb35faf1f, 0xb35faf1f, 0xb35faf1f } }, + { 0xa32c, + { 0xbddfb99f, 0xbddfb99f, 0xbbdfb99f, 0xbbdfb99f, 0xbbdfb99f } }, + { 0xa330, + { 0xcb7fc53f, 0xcb7fc53f, 0xcb7fc73f, 0xcb7fc73f, 0xcb7fc73f } }, + { 0xa334, + { 0xd5ffd1bf, 0xd5ffd1bf, 0xd3ffd1bf, 0xd3ffd1bf, 0xd3ffd1bf } }, +}; + +static const struct ath5k_ini rf5413_ini_common_end[] = { + { AR5K_DCU_FP, 0x000003e0 }, + { AR5K_5414_CBCFG, 0x00000010 }, + { AR5K_SEQ_MASK, 0x0000000f }, + { 0x809c, 0x00000000 }, + { 0x80a0, 0x00000000 }, + { AR5K_MIC_QOS_CTL, 0x00000000 }, + { AR5K_MIC_QOS_SEL, 0x00000000 }, + { AR5K_MISC_MODE, 0x00000000 }, + { AR5K_OFDM_FIL_CNT, 0x00000000 }, + { AR5K_CCK_FIL_CNT, 0x00000000 }, + { AR5K_PHYERR_CNT1, 0x00000000 }, + { AR5K_PHYERR_CNT1_MASK, 0x00000000 }, + { AR5K_PHYERR_CNT2, 0x00000000 }, + { AR5K_PHYERR_CNT2_MASK, 0x00000000 }, + { AR5K_TSF_THRES, 0x00000000 }, + { 0x8140, 0x800003f9 }, + { 0x8144, 0x00000000 }, + { AR5K_PHY_AGC, 0x00000000 }, + { AR5K_PHY_ADC_CTL, 0x0000a000 }, + { 0x983c, 0x00200400 }, + { AR5K_PHY_GAIN_OFFSET, 0x1284233c }, + { AR5K_PHY_SCR, 0x0000001f }, + { AR5K_PHY_SLMT, 0x00000080 }, + { AR5K_PHY_SCAL, 0x0000000e }, + { 0x9958, 0x00081fff }, + { AR5K_PHY_TIMING_7, 0x00000000 }, + { AR5K_PHY_TIMING_8, 0x02800000 }, + { AR5K_PHY_TIMING_11, 0x00000000 }, + { AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000 }, + { 0x99e4, 0xaaaaaaaa }, + { 0x99e8, 0x3c466478 }, + { 0x99ec, 0x000000aa }, + { AR5K_PHY_SCLOCK, 0x0000000c }, + { AR5K_PHY_SDELAY, 0x000000ff }, + { AR5K_PHY_SPENDING, 0x00000014 }, + { AR5K_PHY_DAG_CCK_CTL, 0x000009b5 }, + { 0xa23c, 0x93c889af }, + { AR5K_PHY_FAST_ADC, 0x00000001 }, + { 0xa250, 0x0000a000 }, + { AR5K_PHY_BLUETOOTH, 0x00000000 }, + { AR5K_PHY_TPC_RG1, 0x0cc75380 }, + { 0xa25c, 0x0f0f0f01 }, + { 0xa260, 0x5f690f01 }, + { 0xa264, 0x00418a11 }, + { 0xa268, 0x00000000 }, + { AR5K_PHY_TPC_RG5, 0x0c30c16a }, + { 0xa270, 0x00820820 }, + { 0xa274, 0x081b7caa }, + { 0xa278, 0x1ce739ce }, + { 0xa27c, 0x051701ce }, + { 0xa338, 0x00000000 }, + { 0xa33c, 0x00000000 }, + { 0xa340, 0x00000000 }, + { 0xa344, 0x00000000 }, + { 0xa348, 0x3fffffff }, + { 0xa34c, 0x3fffffff }, + { 0xa350, 0x3fffffff }, + { 0xa354, 0x0003ffff }, + { 0xa358, 0x79a8aa1f }, + { 0xa35c, 0x066c420f }, + { 0xa360, 0x0f282207 }, + { 0xa364, 0x17601685 }, + { 0xa368, 0x1f801104 }, + { 0xa36c, 0x37a00c03 }, + { 0xa370, 0x3fc40883 }, + { 0xa374, 0x57c00803 }, + { 0xa378, 0x5fd80682 }, + { 0xa37c, 0x7fe00482 }, + { 0xa380, 0x7f3c7bba }, + { 0xa384, 0xf3307ff0 }, +}; + +/* Initial mode-specific settings for RF2413/2414 (Written after ar5212_ini) */ +/* XXX: a mode ? */ +static const struct ath5k_ini_mode rf2413_ini_mode_end[] = { + { AR5K_TXCFG, + /* a/XR aTurbo b g (DYN) gTurbo */ + { 0x00000015, 0x00000015, 0x00000015, 0x00000015, 0x00000015 } }, + { AR5K_USEC_5211, + { 0x128d93a7, 0x098813cf, 0x04e01395, 0x12e013ab, 0x098813cf } }, + { AR5K_PHY_RF_CTL3, + { 0x0a020001, 0x0a020001, 0x05020000, 0x0a020001, 0x0a020001 } }, + { AR5K_PHY_RF_CTL4, + { 0x00000e00, 0x00000e00, 0x00000e00, 0x00000e00, 0x00000e00 } }, + { AR5K_PHY_PA_CTL, + { 0x00000002, 0x00000002, 0x0000000a, 0x0000000a, 0x0000000a } }, + { AR5K_PHY_GAIN, + { 0x0018da6d, 0x0018da6d, 0x001a6a64, 0x001a6a64, 0x001a6a64 } }, + { AR5K_PHY_DESIRED_SIZE, + { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b0da, 0x0c98b0da, 0x0de8b0da } }, + { AR5K_PHY_SIG, + { 0x7e800d2e, 0x7e800d2e, 0x7ee80d2e, 0x7ec80d2e, 0x7e800d2e } }, + { AR5K_PHY_AGCCOARSE, + { 0x3137665e, 0x3137665e, 0x3137665e, 0x3139605e, 0x3137665e } }, + { AR5K_PHY_WEAK_OFDM_LOW_THR, + { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } }, + { AR5K_PHY_RX_DELAY, + { 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } }, + { AR5K_PHY_FRAME_CTL_5211, + { 0xf7b81000, 0xf7b81000, 0xf7b80d00, 0xf7b81000, 0xf7b81000 } }, + { AR5K_PHY_CCKTXCTL, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { AR5K_PHY_CCK_CROSSCORR, + { 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } }, + { AR5K_PHY_GAIN_2GHZ, + { 0x002c0140, 0x002c0140, 0x0042c140, 0x0042c140, 0x0042c140 } }, + { AR5K_PHY_CCK_RX_CTL_4, + { 0x1883800a, 0x1883800a, 0x1863800a, 0x1883800a, 0x1883800a } }, +}; + +static const struct ath5k_ini rf2413_ini_common_end[] = { + { AR5K_DCU_FP, 0x000003e0 }, + { AR5K_SEQ_MASK, 0x0000000f }, + { AR5K_MIC_QOS_CTL, 0x00000000 }, + { AR5K_MIC_QOS_SEL, 0x00000000 }, + { AR5K_MISC_MODE, 0x00000000 }, + { AR5K_OFDM_FIL_CNT, 0x00000000 }, + { AR5K_CCK_FIL_CNT, 0x00000000 }, + { AR5K_PHYERR_CNT1, 0x00000000 }, + { AR5K_PHYERR_CNT1_MASK, 0x00000000 }, + { AR5K_PHYERR_CNT2, 0x00000000 }, + { AR5K_PHYERR_CNT2_MASK, 0x00000000 }, + { AR5K_TSF_THRES, 0x00000000 }, + { 0x8140, 0x800000a8 }, + { 0x8144, 0x00000000 }, + { AR5K_PHY_AGC, 0x00000000 }, + { AR5K_PHY_ADC_CTL, 0x0000a000 }, + { 0x983c, 0x00200400 }, + { AR5K_PHY_GAIN_OFFSET, 0x1284233c }, + { AR5K_PHY_SCR, 0x0000001f }, + { AR5K_PHY_SLMT, 0x00000080 }, + { AR5K_PHY_SCAL, 0x0000000e }, + { 0x9958, 0x000000ff }, + { AR5K_PHY_TIMING_7, 0x00000000 }, + { AR5K_PHY_TIMING_8, 0x02800000 }, + { AR5K_PHY_TIMING_11, 0x00000000 }, + { AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000 }, + { 0x99e4, 0xaaaaaaaa }, + { 0x99e8, 0x3c466478 }, + { 0x99ec, 0x000000aa }, + { AR5K_PHY_SCLOCK, 0x0000000c }, + { AR5K_PHY_SDELAY, 0x000000ff }, + { AR5K_PHY_SPENDING, 0x00000014 }, + { AR5K_PHY_DAG_CCK_CTL, 0x000009b5 }, + { 0xa23c, 0x93c889af }, + { AR5K_PHY_FAST_ADC, 0x00000001 }, + { 0xa250, 0x0000a000 }, + { AR5K_PHY_BLUETOOTH, 0x00000000 }, + { AR5K_PHY_TPC_RG1, 0x0cc75380 }, + { 0xa25c, 0x0f0f0f01 }, + { 0xa260, 0x5f690f01 }, + { 0xa264, 0x00418a11 }, + { 0xa268, 0x00000000 }, + { AR5K_PHY_TPC_RG5, 0x0c30c16a }, + { 0xa270, 0x00820820 }, + { 0xa274, 0x001b7caa }, + { 0xa278, 0x1ce739ce }, + { 0xa27c, 0x051701ce }, + { 0xa300, 0x18010000 }, + { 0xa304, 0x30032602 }, + { 0xa308, 0x48073e06 }, + { 0xa30c, 0x560b4c0a }, + { 0xa310, 0x641a600f }, + { 0xa314, 0x784f6e1b }, + { 0xa318, 0x868f7c5a }, + { 0xa31c, 0x8ecf865b }, + { 0xa320, 0x9d4f970f }, + { 0xa324, 0xa5cfa18f }, + { 0xa328, 0xb55faf1f }, + { 0xa32c, 0xbddfb99f }, + { 0xa330, 0xcd7fc73f }, + { 0xa334, 0xd5ffd1bf }, + { 0xa338, 0x00000000 }, + { 0xa33c, 0x00000000 }, + { 0xa340, 0x00000000 }, + { 0xa344, 0x00000000 }, + { 0xa348, 0x3fffffff }, + { 0xa34c, 0x3fffffff }, + { 0xa350, 0x3fffffff }, + { 0xa354, 0x0003ffff }, + { 0xa358, 0x79a8aa1f }, + { 0xa35c, 0x066c420f }, + { 0xa360, 0x0f282207 }, + { 0xa364, 0x17601685 }, + { 0xa368, 0x1f801104 }, + { 0xa36c, 0x37a00c03 }, + { 0xa370, 0x3fc40883 }, + { 0xa374, 0x57c00803 }, + { 0xa378, 0x5fd80682 }, + { 0xa37c, 0x7fe00482 }, + { 0xa380, 0x7f3c7bba }, + { 0xa384, 0xf3307ff0 }, +}; + +/* Initial mode-specific settings for RF2425 (Written after ar5212_ini) */ +/* XXX: a mode ? */ +static const struct ath5k_ini_mode rf2425_ini_mode_end[] = { + { AR5K_TXCFG, + /* a/XR aTurbo b g (DYN) gTurbo */ + { 0x00000015, 0x00000015, 0x00000015, 0x00000015, 0x00000015 } }, + { AR5K_USEC_5211, + { 0x128d93a7, 0x098813cf, 0x04e01395, 0x12e013ab, 0x098813cf } }, + { AR5K_PHY_TURBO, + { 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001 } }, + { AR5K_PHY_RF_CTL3, + { 0x0a020001, 0x0a020001, 0x05020100, 0x0a020001, 0x0a020001 } }, + { AR5K_PHY_RF_CTL4, + { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, + { AR5K_PHY_PA_CTL, + { 0x00000003, 0x00000003, 0x0000000b, 0x0000000b, 0x0000000b } }, + { AR5K_PHY_SETTLING, + { 0x1372161c, 0x13721c25, 0x13721722, 0x13721422, 0x13721c25 } }, + { AR5K_PHY_GAIN, + { 0x0018fa61, 0x0018fa61, 0x00199a65, 0x00199a65, 0x00199a65 } }, + { AR5K_PHY_DESIRED_SIZE, + { 0x0c98b4e0, 0x0c98b4e0, 0x0c98b0da, 0x0c98b0da, 0x0c98b0da } }, + { AR5K_PHY_SIG, + { 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e } }, + { AR5K_PHY_AGCCOARSE, + { 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e } }, + { AR5K_PHY_WEAK_OFDM_LOW_THR, + { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } }, + { AR5K_PHY_RX_DELAY, + { 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } }, + { AR5K_PHY_FRAME_CTL_5211, + { 0xf7b81000, 0xf7b81000, 0xf7b80d00, 0xf7b81000, 0xf7b81000 } }, + { AR5K_PHY_CCKTXCTL, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { AR5K_PHY_CCK_CROSSCORR, + { 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } }, + { AR5K_PHY_GAIN_2GHZ, + { 0x00000140, 0x00000140, 0x0052c140, 0x0052c140, 0x0052c140 } }, + { AR5K_PHY_CCK_RX_CTL_4, + { 0x1883800a, 0x1883800a, 0x1863800a, 0x1883800a, 0x1883800a } }, + { 0xa324, + { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } }, + { 0xa328, + { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } }, + { 0xa32c, + { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } }, + { 0xa330, + { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } }, + { 0xa334, + { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } }, +}; + +static const struct ath5k_ini rf2425_ini_common_end[] = { + { AR5K_DCU_FP, 0x000003e0 }, + { AR5K_SEQ_MASK, 0x0000000f }, + { 0x809c, 0x00000000 }, + { 0x80a0, 0x00000000 }, + { AR5K_MIC_QOS_CTL, 0x00000000 }, + { AR5K_MIC_QOS_SEL, 0x00000000 }, + { AR5K_MISC_MODE, 0x00000000 }, + { AR5K_OFDM_FIL_CNT, 0x00000000 }, + { AR5K_CCK_FIL_CNT, 0x00000000 }, + { AR5K_PHYERR_CNT1, 0x00000000 }, + { AR5K_PHYERR_CNT1_MASK, 0x00000000 }, + { AR5K_PHYERR_CNT2, 0x00000000 }, + { AR5K_PHYERR_CNT2_MASK, 0x00000000 }, + { AR5K_TSF_THRES, 0x00000000 }, + { 0x8140, 0x800003f9 }, + { 0x8144, 0x00000000 }, + { AR5K_PHY_AGC, 0x00000000 }, + { AR5K_PHY_ADC_CTL, 0x0000a000 }, + { 0x983c, 0x00200400 }, + { AR5K_PHY_GAIN_OFFSET, 0x1284233c }, + { AR5K_PHY_SCR, 0x0000001f }, + { AR5K_PHY_SLMT, 0x00000080 }, + { AR5K_PHY_SCAL, 0x0000000e }, + { 0x9958, 0x00081fff }, + { AR5K_PHY_TIMING_7, 0x00000000 }, + { AR5K_PHY_TIMING_8, 0x02800000 }, + { AR5K_PHY_TIMING_11, 0x00000000 }, + { 0x99dc, 0xfebadbe8 }, + { AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000 }, + { 0x99e4, 0xaaaaaaaa }, + { 0x99e8, 0x3c466478 }, + { 0x99ec, 0x000000aa }, + { AR5K_PHY_SCLOCK, 0x0000000c }, + { AR5K_PHY_SDELAY, 0x000000ff }, + { AR5K_PHY_SPENDING, 0x00000014 }, + { AR5K_PHY_DAG_CCK_CTL, 0x000009b5 }, + { AR5K_PHY_TXPOWER_RATE3, 0x20202020 }, + { AR5K_PHY_TXPOWER_RATE4, 0x20202020 }, + { 0xa23c, 0x93c889af }, + { AR5K_PHY_FAST_ADC, 0x00000001 }, + { 0xa250, 0x0000a000 }, + { AR5K_PHY_BLUETOOTH, 0x00000000 }, + { AR5K_PHY_TPC_RG1, 0x0cc75380 }, + { 0xa25c, 0x0f0f0f01 }, + { 0xa260, 0x5f690f01 }, + { 0xa264, 0x00418a11 }, + { 0xa268, 0x00000000 }, + { AR5K_PHY_TPC_RG5, 0x0c30c166 }, + { 0xa270, 0x00820820 }, + { 0xa274, 0x081a3caa }, + { 0xa278, 0x1ce739ce }, + { 0xa27c, 0x051701ce }, + { 0xa300, 0x16010000 }, + { 0xa304, 0x2c032402 }, + { 0xa308, 0x48433e42 }, + { 0xa30c, 0x5a0f500b }, + { 0xa310, 0x6c4b624a }, + { 0xa314, 0x7e8b748a }, + { 0xa318, 0x96cf8ccb }, + { 0xa31c, 0xa34f9d0f }, + { 0xa320, 0xa7cfa58f }, + { 0xa348, 0x3fffffff }, + { 0xa34c, 0x3fffffff }, + { 0xa350, 0x3fffffff }, + { 0xa354, 0x0003ffff }, + { 0xa358, 0x79a8aa1f }, + { 0xa35c, 0x066c420f }, + { 0xa360, 0x0f282207 }, + { 0xa364, 0x17601685 }, + { 0xa368, 0x1f801104 }, + { 0xa36c, 0x37a00c03 }, + { 0xa370, 0x3fc40883 }, + { 0xa374, 0x57c00803 }, + { 0xa378, 0x5fd80682 }, + { 0xa37c, 0x7fe00482 }, + { 0xa380, 0x7f3c7bba }, + { 0xa384, 0xf3307ff0 }, +}; + +/* + * Initial BaseBand Gain settings for RF5111/5112 (AR5210 comes with + * RF5110 only so initial BB Gain settings are included in AR5K_AR5210_INI) + */ + +/* RF5111 Initial BaseBand Gain settings */ +static const struct ath5k_ini rf5111_ini_bbgain[] = { + { AR5K_BB_GAIN(0), 0x00000000 }, + { AR5K_BB_GAIN(1), 0x00000020 }, + { AR5K_BB_GAIN(2), 0x00000010 }, + { AR5K_BB_GAIN(3), 0x00000030 }, + { AR5K_BB_GAIN(4), 0x00000008 }, + { AR5K_BB_GAIN(5), 0x00000028 }, + { AR5K_BB_GAIN(6), 0x00000004 }, + { AR5K_BB_GAIN(7), 0x00000024 }, + { AR5K_BB_GAIN(8), 0x00000014 }, + { AR5K_BB_GAIN(9), 0x00000034 }, + { AR5K_BB_GAIN(10), 0x0000000c }, + { AR5K_BB_GAIN(11), 0x0000002c }, + { AR5K_BB_GAIN(12), 0x00000002 }, + { AR5K_BB_GAIN(13), 0x00000022 }, + { AR5K_BB_GAIN(14), 0x00000012 }, + { AR5K_BB_GAIN(15), 0x00000032 }, + { AR5K_BB_GAIN(16), 0x0000000a }, + { AR5K_BB_GAIN(17), 0x0000002a }, + { AR5K_BB_GAIN(18), 0x00000006 }, + { AR5K_BB_GAIN(19), 0x00000026 }, + { AR5K_BB_GAIN(20), 0x00000016 }, + { AR5K_BB_GAIN(21), 0x00000036 }, + { AR5K_BB_GAIN(22), 0x0000000e }, + { AR5K_BB_GAIN(23), 0x0000002e }, + { AR5K_BB_GAIN(24), 0x00000001 }, + { AR5K_BB_GAIN(25), 0x00000021 }, + { AR5K_BB_GAIN(26), 0x00000011 }, + { AR5K_BB_GAIN(27), 0x00000031 }, + { AR5K_BB_GAIN(28), 0x00000009 }, + { AR5K_BB_GAIN(29), 0x00000029 }, + { AR5K_BB_GAIN(30), 0x00000005 }, + { AR5K_BB_GAIN(31), 0x00000025 }, + { AR5K_BB_GAIN(32), 0x00000015 }, + { AR5K_BB_GAIN(33), 0x00000035 }, + { AR5K_BB_GAIN(34), 0x0000000d }, + { AR5K_BB_GAIN(35), 0x0000002d }, + { AR5K_BB_GAIN(36), 0x00000003 }, + { AR5K_BB_GAIN(37), 0x00000023 }, + { AR5K_BB_GAIN(38), 0x00000013 }, + { AR5K_BB_GAIN(39), 0x00000033 }, + { AR5K_BB_GAIN(40), 0x0000000b }, + { AR5K_BB_GAIN(41), 0x0000002b }, + { AR5K_BB_GAIN(42), 0x0000002b }, + { AR5K_BB_GAIN(43), 0x0000002b }, + { AR5K_BB_GAIN(44), 0x0000002b }, + { AR5K_BB_GAIN(45), 0x0000002b }, + { AR5K_BB_GAIN(46), 0x0000002b }, + { AR5K_BB_GAIN(47), 0x0000002b }, + { AR5K_BB_GAIN(48), 0x0000002b }, + { AR5K_BB_GAIN(49), 0x0000002b }, + { AR5K_BB_GAIN(50), 0x0000002b }, + { AR5K_BB_GAIN(51), 0x0000002b }, + { AR5K_BB_GAIN(52), 0x0000002b }, + { AR5K_BB_GAIN(53), 0x0000002b }, + { AR5K_BB_GAIN(54), 0x0000002b }, + { AR5K_BB_GAIN(55), 0x0000002b }, + { AR5K_BB_GAIN(56), 0x0000002b }, + { AR5K_BB_GAIN(57), 0x0000002b }, + { AR5K_BB_GAIN(58), 0x0000002b }, + { AR5K_BB_GAIN(59), 0x0000002b }, + { AR5K_BB_GAIN(60), 0x0000002b }, + { AR5K_BB_GAIN(61), 0x0000002b }, + { AR5K_BB_GAIN(62), 0x00000002 }, + { AR5K_BB_GAIN(63), 0x00000016 }, +}; + +/* RF5112 Initial BaseBand Gain settings (Same for RF5413/5414+) */ +static const struct ath5k_ini rf5112_ini_bbgain[] = { + { AR5K_BB_GAIN(0), 0x00000000 }, + { AR5K_BB_GAIN(1), 0x00000001 }, + { AR5K_BB_GAIN(2), 0x00000002 }, + { AR5K_BB_GAIN(3), 0x00000003 }, + { AR5K_BB_GAIN(4), 0x00000004 }, + { AR5K_BB_GAIN(5), 0x00000005 }, + { AR5K_BB_GAIN(6), 0x00000008 }, + { AR5K_BB_GAIN(7), 0x00000009 }, + { AR5K_BB_GAIN(8), 0x0000000a }, + { AR5K_BB_GAIN(9), 0x0000000b }, + { AR5K_BB_GAIN(10), 0x0000000c }, + { AR5K_BB_GAIN(11), 0x0000000d }, + { AR5K_BB_GAIN(12), 0x00000010 }, + { AR5K_BB_GAIN(13), 0x00000011 }, + { AR5K_BB_GAIN(14), 0x00000012 }, + { AR5K_BB_GAIN(15), 0x00000013 }, + { AR5K_BB_GAIN(16), 0x00000014 }, + { AR5K_BB_GAIN(17), 0x00000015 }, + { AR5K_BB_GAIN(18), 0x00000018 }, + { AR5K_BB_GAIN(19), 0x00000019 }, + { AR5K_BB_GAIN(20), 0x0000001a }, + { AR5K_BB_GAIN(21), 0x0000001b }, + { AR5K_BB_GAIN(22), 0x0000001c }, + { AR5K_BB_GAIN(23), 0x0000001d }, + { AR5K_BB_GAIN(24), 0x00000020 }, + { AR5K_BB_GAIN(25), 0x00000021 }, + { AR5K_BB_GAIN(26), 0x00000022 }, + { AR5K_BB_GAIN(27), 0x00000023 }, + { AR5K_BB_GAIN(28), 0x00000024 }, + { AR5K_BB_GAIN(29), 0x00000025 }, + { AR5K_BB_GAIN(30), 0x00000028 }, + { AR5K_BB_GAIN(31), 0x00000029 }, + { AR5K_BB_GAIN(32), 0x0000002a }, + { AR5K_BB_GAIN(33), 0x0000002b }, + { AR5K_BB_GAIN(34), 0x0000002c }, + { AR5K_BB_GAIN(35), 0x0000002d }, + { AR5K_BB_GAIN(36), 0x00000030 }, + { AR5K_BB_GAIN(37), 0x00000031 }, + { AR5K_BB_GAIN(38), 0x00000032 }, + { AR5K_BB_GAIN(39), 0x00000033 }, + { AR5K_BB_GAIN(40), 0x00000034 }, + { AR5K_BB_GAIN(41), 0x00000035 }, + { AR5K_BB_GAIN(42), 0x00000035 }, + { AR5K_BB_GAIN(43), 0x00000035 }, + { AR5K_BB_GAIN(44), 0x00000035 }, + { AR5K_BB_GAIN(45), 0x00000035 }, + { AR5K_BB_GAIN(46), 0x00000035 }, + { AR5K_BB_GAIN(47), 0x00000035 }, + { AR5K_BB_GAIN(48), 0x00000035 }, + { AR5K_BB_GAIN(49), 0x00000035 }, + { AR5K_BB_GAIN(50), 0x00000035 }, + { AR5K_BB_GAIN(51), 0x00000035 }, + { AR5K_BB_GAIN(52), 0x00000035 }, + { AR5K_BB_GAIN(53), 0x00000035 }, + { AR5K_BB_GAIN(54), 0x00000035 }, + { AR5K_BB_GAIN(55), 0x00000035 }, + { AR5K_BB_GAIN(56), 0x00000035 }, + { AR5K_BB_GAIN(57), 0x00000035 }, + { AR5K_BB_GAIN(58), 0x00000035 }, + { AR5K_BB_GAIN(59), 0x00000035 }, + { AR5K_BB_GAIN(60), 0x00000035 }, + { AR5K_BB_GAIN(61), 0x00000035 }, + { AR5K_BB_GAIN(62), 0x00000010 }, + { AR5K_BB_GAIN(63), 0x0000001a }, +}; + + +/* + * Write initial register dump + */ +static void ath5k_hw_ini_registers(struct ath5k_hw *ah, unsigned int size, + const struct ath5k_ini *ini_regs, bool change_channel) +{ + unsigned int i; + + /* Write initial registers */ + for (i = 0; i < size; i++) { + /* On channel change there is + * no need to mess with PCU */ + if (change_channel && + ini_regs[i].ini_register >= AR5K_PCU_MIN && + ini_regs[i].ini_register <= AR5K_PCU_MAX) + continue; + + switch (ini_regs[i].ini_mode) { + case AR5K_INI_READ: + /* Cleared on read */ + ath5k_hw_reg_read(ah, ini_regs[i].ini_register); + break; + case AR5K_INI_WRITE: + default: + AR5K_REG_WAIT(i); + ath5k_hw_reg_write(ah, ini_regs[i].ini_value, + ini_regs[i].ini_register); + } + } +} + +static void ath5k_hw_ini_mode_registers(struct ath5k_hw *ah, + unsigned int size, const struct ath5k_ini_mode *ini_mode, + u8 mode) +{ + unsigned int i; + + for (i = 0; i < size; i++) { + AR5K_REG_WAIT(i); + ath5k_hw_reg_write(ah, ini_mode[i].mode_value[mode], + (u32)ini_mode[i].mode_register); + } + +} + +int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel) +{ + /* + * Write initial register settings + */ + + /* For AR5212 and combatible */ + if (ah->ah_version == AR5K_AR5212) { + + /* First set of mode-specific settings */ + ath5k_hw_ini_mode_registers(ah, + ARRAY_SIZE(ar5212_ini_mode_start), + ar5212_ini_mode_start, mode); + + /* + * Write initial settings common for all modes + */ + ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5212_ini_common_start), + ar5212_ini_common_start, change_channel); + + /* Second set of mode-specific settings */ + switch (ah->ah_radio) { + case AR5K_RF5111: + + ath5k_hw_ini_mode_registers(ah, + ARRAY_SIZE(rf5111_ini_mode_end), + rf5111_ini_mode_end, mode); + + ath5k_hw_ini_registers(ah, + ARRAY_SIZE(rf5111_ini_common_end), + rf5111_ini_common_end, change_channel); + + /* Baseband gain table */ + ath5k_hw_ini_registers(ah, + ARRAY_SIZE(rf5111_ini_bbgain), + rf5111_ini_bbgain, change_channel); + + break; + case AR5K_RF5112: + + ath5k_hw_ini_mode_registers(ah, + ARRAY_SIZE(rf5112_ini_mode_end), + rf5112_ini_mode_end, mode); + + ath5k_hw_ini_registers(ah, + ARRAY_SIZE(rf5112_ini_common_end), + rf5112_ini_common_end, change_channel); + + ath5k_hw_ini_registers(ah, + ARRAY_SIZE(rf5112_ini_bbgain), + rf5112_ini_bbgain, change_channel); + + break; + case AR5K_RF5413: + + ath5k_hw_ini_mode_registers(ah, + ARRAY_SIZE(rf5413_ini_mode_end), + rf5413_ini_mode_end, mode); + + ath5k_hw_ini_registers(ah, + ARRAY_SIZE(rf5413_ini_common_end), + rf5413_ini_common_end, change_channel); + + ath5k_hw_ini_registers(ah, + ARRAY_SIZE(rf5112_ini_bbgain), + rf5112_ini_bbgain, change_channel); + + break; + case AR5K_RF2316: + case AR5K_RF2413: + + ath5k_hw_ini_mode_registers(ah, + ARRAY_SIZE(rf2413_ini_mode_end), + rf2413_ini_mode_end, mode); + + ath5k_hw_ini_registers(ah, + ARRAY_SIZE(rf2413_ini_common_end), + rf2413_ini_common_end, change_channel); + + /* Override settings from rf2413_ini_common_end */ + if (ah->ah_radio == AR5K_RF2316) { + ath5k_hw_reg_write(ah, 0x00004000, + AR5K_PHY_AGC); + ath5k_hw_reg_write(ah, 0x081b7caa, + 0xa274); + } + + ath5k_hw_ini_registers(ah, + ARRAY_SIZE(rf5112_ini_bbgain), + rf5112_ini_bbgain, change_channel); + break; + case AR5K_RF2317: + case AR5K_RF2425: + + ath5k_hw_ini_mode_registers(ah, + ARRAY_SIZE(rf2425_ini_mode_end), + rf2425_ini_mode_end, mode); + + ath5k_hw_ini_registers(ah, + ARRAY_SIZE(rf2425_ini_common_end), + rf2425_ini_common_end, change_channel); + + ath5k_hw_ini_registers(ah, + ARRAY_SIZE(rf5112_ini_bbgain), + rf5112_ini_bbgain, change_channel); + break; + default: + return -EINVAL; + + } + + /* For AR5211 */ + } else if (ah->ah_version == AR5K_AR5211) { + + /* AR5K_MODE_11B */ + if (mode > 2) { + ATH5K_ERR(ah->ah_sc, + "unsupported channel mode: %d\n", mode); + return -EINVAL; + } + + /* Mode-specific settings */ + ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(ar5211_ini_mode), + ar5211_ini_mode, mode); + + /* + * Write initial settings common for all modes + */ + ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5211_ini), + ar5211_ini, change_channel); + + /* AR5211 only comes with 5111 */ + + /* Baseband gain table */ + ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5111_ini_bbgain), + rf5111_ini_bbgain, change_channel); + /* For AR5210 (for mode settings check out ath5k_hw_reset_tx_queue) */ + } else if (ah->ah_version == AR5K_AR5210) { + ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5210_ini), + ar5210_ini, change_channel); + } + + return 0; +} diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c new file mode 100644 index 00000000000..876725f08b6 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2005 Atheros Communications, Inc. + * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com> + * Copyright (c) 2009 Bob Copeland <me@bobcopeland.com> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include <linux/pci.h> +#include "ath5k.h" +#include "base.h" + +#define ATH_SDEVICE(subv,subd) \ + .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \ + .subvendor = (subv), .subdevice = (subd) + +#define ATH_LED(pin,polarity) .driver_data = (((pin) << 8) | (polarity)) +#define ATH_PIN(data) ((data) >> 8) +#define ATH_POLARITY(data) ((data) & 0xff) + +/* Devices we match on for LED config info (typically laptops) */ +static const struct pci_device_id ath5k_led_devices[] = { + /* AR5211 */ + { PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5211), ATH_LED(0, 0) }, + /* HP Compaq nc6xx, nc4000, nx6000 */ + { ATH_SDEVICE(PCI_VENDOR_ID_COMPAQ, PCI_ANY_ID), ATH_LED(1, 1) }, + /* Acer Aspire One A150 (maximlevitsky@gmail.com) */ + { ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe008), ATH_LED(3, 0) }, + /* Acer Ferrari 5000 (russ.dill@gmail.com) */ + { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) }, + /* E-machines E510 (tuliom@gmail.com) */ + { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0428), ATH_LED(3, 0) }, + /* Acer Extensa 5620z (nekoreeve@gmail.com) */ + { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0105), ATH_LED(3, 0) }, + /* Fukato Datacask Jupiter 1014a (mrb74@gmx.at) */ + { ATH_SDEVICE(PCI_VENDOR_ID_AZWAVE, 0x1026), ATH_LED(3, 0) }, + /* IBM ThinkPad AR5BXB6 (legovini@spiro.fisica.unipd.it) */ + { ATH_SDEVICE(PCI_VENDOR_ID_IBM, 0x058a), ATH_LED(1, 0) }, + /* IBM-specific AR5212 (all others) */ + { PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) }, + { } +}; + +void ath5k_led_enable(struct ath5k_softc *sc) +{ + if (test_bit(ATH_STAT_LEDSOFT, sc->status)) { + ath5k_hw_set_gpio_output(sc->ah, sc->led_pin); + ath5k_led_off(sc); + } +} + +static void ath5k_led_on(struct ath5k_softc *sc) +{ + if (!test_bit(ATH_STAT_LEDSOFT, sc->status)) + return; + ath5k_hw_set_gpio(sc->ah, sc->led_pin, sc->led_on); +} + +void ath5k_led_off(struct ath5k_softc *sc) +{ + if (!test_bit(ATH_STAT_LEDSOFT, sc->status)) + return; + ath5k_hw_set_gpio(sc->ah, sc->led_pin, !sc->led_on); +} + +static void +ath5k_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct ath5k_led *led = container_of(led_dev, struct ath5k_led, + led_dev); + + if (brightness == LED_OFF) + ath5k_led_off(led->sc); + else + ath5k_led_on(led->sc); +} + +static int +ath5k_register_led(struct ath5k_softc *sc, struct ath5k_led *led, + const char *name, char *trigger) +{ + int err; + + led->sc = sc; + strncpy(led->name, name, sizeof(led->name)); + led->led_dev.name = led->name; + led->led_dev.default_trigger = trigger; + led->led_dev.brightness_set = ath5k_led_brightness_set; + + err = led_classdev_register(&sc->pdev->dev, &led->led_dev); + if (err) { + ATH5K_WARN(sc, "could not register LED %s\n", name); + led->sc = NULL; + } + return err; +} + +static void +ath5k_unregister_led(struct ath5k_led *led) +{ + if (!led->sc) + return; + led_classdev_unregister(&led->led_dev); + ath5k_led_off(led->sc); + led->sc = NULL; +} + +void ath5k_unregister_leds(struct ath5k_softc *sc) +{ + ath5k_unregister_led(&sc->rx_led); + ath5k_unregister_led(&sc->tx_led); +} + +int ath5k_init_leds(struct ath5k_softc *sc) +{ + int ret = 0; + struct ieee80211_hw *hw = sc->hw; + struct pci_dev *pdev = sc->pdev; + char name[ATH5K_LED_MAX_NAME_LEN + 1]; + const struct pci_device_id *match; + + match = pci_match_id(&ath5k_led_devices[0], pdev); + if (match) { + __set_bit(ATH_STAT_LEDSOFT, sc->status); + sc->led_pin = ATH_PIN(match->driver_data); + sc->led_on = ATH_POLARITY(match->driver_data); + } + + if (!test_bit(ATH_STAT_LEDSOFT, sc->status)) + goto out; + + ath5k_led_enable(sc); + + snprintf(name, sizeof(name), "ath5k-%s::rx", wiphy_name(hw->wiphy)); + ret = ath5k_register_led(sc, &sc->rx_led, name, + ieee80211_get_rx_led_name(hw)); + if (ret) + goto out; + + snprintf(name, sizeof(name), "ath5k-%s::tx", wiphy_name(hw->wiphy)); + ret = ath5k_register_led(sc, &sc->tx_led, name, + ieee80211_get_tx_led_name(hw)); +out: + return ret; +} + diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c new file mode 100644 index 00000000000..ec35503f6a4 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -0,0 +1,1174 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2007-2008 Matthew W. S. Bell <mentor@madwifi.org> + * Copyright (c) 2007-2008 Luis Rodriguez <mcgrof@winlab.rutgers.edu> + * Copyright (c) 2007-2008 Pavel Roskin <proski@gnu.org> + * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +/*********************************\ +* Protocol Control Unit Functions * +\*********************************/ + +#include "ath5k.h" +#include "reg.h" +#include "debug.h" +#include "base.h" + +/*******************\ +* Generic functions * +\*******************/ + +/** + * ath5k_hw_set_opmode - Set PCU operating mode + * + * @ah: The &struct ath5k_hw + * + * Initialize PCU for the various operating modes (AP/STA etc) + * + * NOTE: ah->ah_op_mode must be set before calling this. + */ +int ath5k_hw_set_opmode(struct ath5k_hw *ah) +{ + u32 pcu_reg, beacon_reg, low_id, high_id; + + + /* Preserve rest settings */ + pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; + pcu_reg &= ~(AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_AP + | AR5K_STA_ID1_KEYSRCH_MODE + | (ah->ah_version == AR5K_AR5210 ? + (AR5K_STA_ID1_PWR_SV | AR5K_STA_ID1_NO_PSPOLL) : 0)); + + beacon_reg = 0; + + ATH5K_TRACE(ah->ah_sc); + + switch (ah->ah_op_mode) { + case NL80211_IFTYPE_ADHOC: + pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_KEYSRCH_MODE; + beacon_reg |= AR5K_BCR_ADHOC; + if (ah->ah_version == AR5K_AR5210) + pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; + else + AR5K_REG_ENABLE_BITS(ah, AR5K_CFG, AR5K_CFG_IBSS); + break; + + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE; + beacon_reg |= AR5K_BCR_AP; + if (ah->ah_version == AR5K_AR5210) + pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; + else + AR5K_REG_DISABLE_BITS(ah, AR5K_CFG, AR5K_CFG_IBSS); + break; + + case NL80211_IFTYPE_STATION: + pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE + | (ah->ah_version == AR5K_AR5210 ? + AR5K_STA_ID1_PWR_SV : 0); + case NL80211_IFTYPE_MONITOR: + pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE + | (ah->ah_version == AR5K_AR5210 ? + AR5K_STA_ID1_NO_PSPOLL : 0); + break; + + default: + return -EINVAL; + } + + /* + * Set PCU registers + */ + low_id = AR5K_LOW_ID(ah->ah_sta_id); + high_id = AR5K_HIGH_ID(ah->ah_sta_id); + ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); + ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); + + /* + * Set Beacon Control Register on 5210 + */ + if (ah->ah_version == AR5K_AR5210) + ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR); + + return 0; +} + +/** + * ath5k_hw_update - Update mib counters (mac layer statistics) + * + * @ah: The &struct ath5k_hw + * @stats: The &struct ieee80211_low_level_stats we use to track + * statistics on the driver + * + * Reads MIB counters from PCU and updates sw statistics. Must be + * called after a MIB interrupt. + */ +void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, + struct ieee80211_low_level_stats *stats) +{ + ATH5K_TRACE(ah->ah_sc); + + /* Read-And-Clear */ + stats->dot11ACKFailureCount += ath5k_hw_reg_read(ah, AR5K_ACK_FAIL); + stats->dot11RTSFailureCount += ath5k_hw_reg_read(ah, AR5K_RTS_FAIL); + stats->dot11RTSSuccessCount += ath5k_hw_reg_read(ah, AR5K_RTS_OK); + stats->dot11FCSErrorCount += ath5k_hw_reg_read(ah, AR5K_FCS_FAIL); + + /* XXX: Should we use this to track beacon count ? + * -we read it anyway to clear the register */ + ath5k_hw_reg_read(ah, AR5K_BEACON_CNT); + + /* Reset profile count registers on 5212*/ + if (ah->ah_version == AR5K_AR5212) { + ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_TX); + ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RX); + ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR); + ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE); + } + + /* TODO: Handle ANI stats */ +} + +/** + * ath5k_hw_set_ack_bitrate - set bitrate for ACKs + * + * @ah: The &struct ath5k_hw + * @high: Flag to determine if we want to use high transmition rate + * for ACKs or not + * + * If high flag is set, we tell hw to use a set of control rates based on + * the current transmition rate (check out control_rates array inside reset.c). + * If not hw just uses the lowest rate available for the current modulation + * scheme being used (1Mbit for CCK and 6Mbits for OFDM). + */ +void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high) +{ + if (ah->ah_version != AR5K_AR5212) + return; + else { + u32 val = AR5K_STA_ID1_BASE_RATE_11B | AR5K_STA_ID1_ACKCTS_6MB; + if (high) + AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, val); + else + AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, val); + } +} + + +/******************\ +* ACK/CTS Timeouts * +\******************/ + +/** + * ath5k_hw_het_ack_timeout - Get ACK timeout from PCU in usec + * + * @ah: The &struct ath5k_hw + */ +unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + + return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, + AR5K_TIME_OUT), AR5K_TIME_OUT_ACK), ah->ah_turbo); +} + +/** + * ath5k_hw_set_ack_timeout - Set ACK timeout on PCU + * + * @ah: The &struct ath5k_hw + * @timeout: Timeout in usec + */ +int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) +{ + ATH5K_TRACE(ah->ah_sc); + if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK), + ah->ah_turbo) <= timeout) + return -EINVAL; + + AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK, + ath5k_hw_htoclock(timeout, ah->ah_turbo)); + + return 0; +} + +/** + * ath5k_hw_get_cts_timeout - Get CTS timeout from PCU in usec + * + * @ah: The &struct ath5k_hw + */ +unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, + AR5K_TIME_OUT), AR5K_TIME_OUT_CTS), ah->ah_turbo); +} + +/** + * ath5k_hw_set_cts_timeout - Set CTS timeout on PCU + * + * @ah: The &struct ath5k_hw + * @timeout: Timeout in usec + */ +int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) +{ + ATH5K_TRACE(ah->ah_sc); + if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS), + ah->ah_turbo) <= timeout) + return -EINVAL; + + AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS, + ath5k_hw_htoclock(timeout, ah->ah_turbo)); + + return 0; +} + + +/****************\ +* BSSID handling * +\****************/ + +/** + * ath5k_hw_get_lladdr - Get station id + * + * @ah: The &struct ath5k_hw + * @mac: The card's mac address + * + * Initialize ah->ah_sta_id using the mac address provided + * (just a memcpy). + * + * TODO: Remove it once we merge ath5k_softc and ath5k_hw + */ +void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac) +{ + ATH5K_TRACE(ah->ah_sc); + memcpy(mac, ah->ah_sta_id, ETH_ALEN); +} + +/** + * ath5k_hw_set_lladdr - Set station id + * + * @ah: The &struct ath5k_hw + * @mac: The card's mac address + * + * Set station id on hw using the provided mac address + */ +int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) +{ + u32 low_id, high_id; + u32 pcu_reg; + + ATH5K_TRACE(ah->ah_sc); + /* Set new station ID */ + memcpy(ah->ah_sta_id, mac, ETH_ALEN); + + pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; + + low_id = AR5K_LOW_ID(mac); + high_id = AR5K_HIGH_ID(mac); + + ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); + ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); + + return 0; +} + +/** + * ath5k_hw_set_associd - Set BSSID for association + * + * @ah: The &struct ath5k_hw + * @bssid: BSSID + * @assoc_id: Assoc id + * + * Sets the BSSID which trigers the "SME Join" operation + */ +void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id) +{ + u32 low_id, high_id; + u16 tim_offset = 0; + + /* + * Set simple BSSID mask on 5212 + */ + if (ah->ah_version == AR5K_AR5212) { + ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_bssid_mask), + AR5K_BSS_IDM0); + ath5k_hw_reg_write(ah, AR5K_HIGH_ID(ah->ah_bssid_mask), + AR5K_BSS_IDM1); + } + + /* + * Set BSSID which triggers the "SME Join" operation + */ + low_id = AR5K_LOW_ID(bssid); + high_id = AR5K_HIGH_ID(bssid); + ath5k_hw_reg_write(ah, low_id, AR5K_BSS_ID0); + ath5k_hw_reg_write(ah, high_id | ((assoc_id & 0x3fff) << + AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1); + + if (assoc_id == 0) { + ath5k_hw_disable_pspoll(ah); + return; + } + + AR5K_REG_WRITE_BITS(ah, AR5K_BEACON, AR5K_BEACON_TIM, + tim_offset ? tim_offset + 4 : 0); + + ath5k_hw_enable_pspoll(ah, NULL, 0); +} + +/** + * ath5k_hw_set_bssid_mask - filter out bssids we listen + * + * @ah: the &struct ath5k_hw + * @mask: the bssid_mask, a u8 array of size ETH_ALEN + * + * BSSID masking is a method used by AR5212 and newer hardware to inform PCU + * which bits of the interface's MAC address should be looked at when trying + * to decide which packets to ACK. In station mode and AP mode with a single + * BSS every bit matters since we lock to only one BSS. In AP mode with + * multiple BSSes (virtual interfaces) not every bit matters because hw must + * accept frames for all BSSes and so we tweak some bits of our mac address + * in order to have multiple BSSes. + * + * NOTE: This is a simple filter and does *not* filter out all + * relevant frames. Some frames that are not for us might get ACKed from us + * by PCU because they just match the mask. + * + * When handling multiple BSSes you can get the BSSID mask by computing the + * set of ~ ( MAC XOR BSSID ) for all bssids we handle. + * + * When you do this you are essentially computing the common bits of all your + * BSSes. Later it is assumed the harware will "and" (&) the BSSID mask with + * the MAC address to obtain the relevant bits and compare the result with + * (frame's BSSID & mask) to see if they match. + */ +/* + * Simple example: on your card you have have two BSSes you have created with + * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address. + * There is another BSSID-03 but you are not part of it. For simplicity's sake, + * assuming only 4 bits for a mac address and for BSSIDs you can then have: + * + * \ + * MAC: 0001 | + * BSSID-01: 0100 | --> Belongs to us + * BSSID-02: 1001 | + * / + * ------------------- + * BSSID-03: 0110 | --> External + * ------------------- + * + * Our bssid_mask would then be: + * + * On loop iteration for BSSID-01: + * ~(0001 ^ 0100) -> ~(0101) + * -> 1010 + * bssid_mask = 1010 + * + * On loop iteration for BSSID-02: + * bssid_mask &= ~(0001 ^ 1001) + * bssid_mask = (1010) & ~(0001 ^ 1001) + * bssid_mask = (1010) & ~(1001) + * bssid_mask = (1010) & (0110) + * bssid_mask = 0010 + * + * A bssid_mask of 0010 means "only pay attention to the second least + * significant bit". This is because its the only bit common + * amongst the MAC and all BSSIDs we support. To findout what the real + * common bit is we can simply "&" the bssid_mask now with any BSSID we have + * or our MAC address (we assume the hardware uses the MAC address). + * + * Now, suppose there's an incoming frame for BSSID-03: + * + * IFRAME-01: 0110 + * + * An easy eye-inspeciton of this already should tell you that this frame + * will not pass our check. This is beacuse the bssid_mask tells the + * hardware to only look at the second least significant bit and the + * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB + * as 1, which does not match 0. + * + * So with IFRAME-01 we *assume* the hardware will do: + * + * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; + * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0; + * --> allow = (0010) == 0000 ? 1 : 0; + * --> allow = 0 + * + * Lets now test a frame that should work: + * + * IFRAME-02: 0001 (we should allow) + * + * allow = (0001 & 1010) == 1010 + * + * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; + * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0; + * --> allow = (0010) == (0010) + * --> allow = 1 + * + * Other examples: + * + * IFRAME-03: 0100 --> allowed + * IFRAME-04: 1001 --> allowed + * IFRAME-05: 1101 --> allowed but its not for us!!! + * + */ +int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) +{ + u32 low_id, high_id; + ATH5K_TRACE(ah->ah_sc); + + /* Cache bssid mask so that we can restore it + * on reset */ + memcpy(ah->ah_bssid_mask, mask, ETH_ALEN); + if (ah->ah_version == AR5K_AR5212) { + low_id = AR5K_LOW_ID(mask); + high_id = AR5K_HIGH_ID(mask); + + ath5k_hw_reg_write(ah, low_id, AR5K_BSS_IDM0); + ath5k_hw_reg_write(ah, high_id, AR5K_BSS_IDM1); + + return 0; + } + + return -EIO; +} + + +/************\ +* RX Control * +\************/ + +/** + * ath5k_hw_start_rx_pcu - Start RX engine + * + * @ah: The &struct ath5k_hw + * + * Starts RX engine on PCU so that hw can process RXed frames + * (ACK etc). + * + * NOTE: RX DMA should be already enabled using ath5k_hw_start_rx_dma + * TODO: Init ANI here + */ +void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); +} + +/** + * at5k_hw_stop_rx_pcu - Stop RX engine + * + * @ah: The &struct ath5k_hw + * + * Stops RX engine on PCU + * + * TODO: Detach ANI here + */ +void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); +} + +/* + * Set multicast filter + */ +void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1) +{ + ATH5K_TRACE(ah->ah_sc); + /* Set the multicat filter */ + ath5k_hw_reg_write(ah, filter0, AR5K_MCAST_FILTER0); + ath5k_hw_reg_write(ah, filter1, AR5K_MCAST_FILTER1); +} + +/* + * Set multicast filter by index + */ +int ath5k_hw_set_mcast_filter_idx(struct ath5k_hw *ah, u32 index) +{ + + ATH5K_TRACE(ah->ah_sc); + if (index >= 64) + return -EINVAL; + else if (index >= 32) + AR5K_REG_ENABLE_BITS(ah, AR5K_MCAST_FILTER1, + (1 << (index - 32))); + else + AR5K_REG_ENABLE_BITS(ah, AR5K_MCAST_FILTER0, (1 << index)); + + return 0; +} + +/* + * Clear Multicast filter by index + */ +int ath5k_hw_clear_mcast_filter_idx(struct ath5k_hw *ah, u32 index) +{ + + ATH5K_TRACE(ah->ah_sc); + if (index >= 64) + return -EINVAL; + else if (index >= 32) + AR5K_REG_DISABLE_BITS(ah, AR5K_MCAST_FILTER1, + (1 << (index - 32))); + else + AR5K_REG_DISABLE_BITS(ah, AR5K_MCAST_FILTER0, (1 << index)); + + return 0; +} + +/** + * ath5k_hw_get_rx_filter - Get current rx filter + * + * @ah: The &struct ath5k_hw + * + * Returns the RX filter by reading rx filter and + * phy error filter registers. RX filter is used + * to set the allowed frame types that PCU will accept + * and pass to the driver. For a list of frame types + * check out reg.h. + */ +u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah) +{ + u32 data, filter = 0; + + ATH5K_TRACE(ah->ah_sc); + filter = ath5k_hw_reg_read(ah, AR5K_RX_FILTER); + + /*Radar detection for 5212*/ + if (ah->ah_version == AR5K_AR5212) { + data = ath5k_hw_reg_read(ah, AR5K_PHY_ERR_FIL); + + if (data & AR5K_PHY_ERR_FIL_RADAR) + filter |= AR5K_RX_FILTER_RADARERR; + if (data & (AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK)) + filter |= AR5K_RX_FILTER_PHYERR; + } + + return filter; +} + +/** + * ath5k_hw_set_rx_filter - Set rx filter + * + * @ah: The &struct ath5k_hw + * @filter: RX filter mask (see reg.h) + * + * Sets RX filter register and also handles PHY error filter + * register on 5212 and newer chips so that we have proper PHY + * error reporting. + */ +void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter) +{ + u32 data = 0; + + ATH5K_TRACE(ah->ah_sc); + + /* Set PHY error filter register on 5212*/ + if (ah->ah_version == AR5K_AR5212) { + if (filter & AR5K_RX_FILTER_RADARERR) + data |= AR5K_PHY_ERR_FIL_RADAR; + if (filter & AR5K_RX_FILTER_PHYERR) + data |= AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK; + } + + /* + * The AR5210 uses promiscous mode to detect radar activity + */ + if (ah->ah_version == AR5K_AR5210 && + (filter & AR5K_RX_FILTER_RADARERR)) { + filter &= ~AR5K_RX_FILTER_RADARERR; + filter |= AR5K_RX_FILTER_PROM; + } + + /*Zero length DMA (phy error reporting) */ + if (data) + AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); + else + AR5K_REG_DISABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); + + /*Write RX Filter register*/ + ath5k_hw_reg_write(ah, filter & 0xff, AR5K_RX_FILTER); + + /*Write PHY error filter register on 5212*/ + if (ah->ah_version == AR5K_AR5212) + ath5k_hw_reg_write(ah, data, AR5K_PHY_ERR_FIL); + +} + + +/****************\ +* Beacon control * +\****************/ + +/** + * ath5k_hw_get_tsf32 - Get a 32bit TSF + * + * @ah: The &struct ath5k_hw + * + * Returns lower 32 bits of current TSF + */ +u32 ath5k_hw_get_tsf32(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + return ath5k_hw_reg_read(ah, AR5K_TSF_L32); +} + +/** + * ath5k_hw_get_tsf64 - Get the full 64bit TSF + * + * @ah: The &struct ath5k_hw + * + * Returns the current TSF + */ +u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah) +{ + u64 tsf = ath5k_hw_reg_read(ah, AR5K_TSF_U32); + ATH5K_TRACE(ah->ah_sc); + + return ath5k_hw_reg_read(ah, AR5K_TSF_L32) | (tsf << 32); +} + +/** + * ath5k_hw_set_tsf64 - Set a new 64bit TSF + * + * @ah: The &struct ath5k_hw + * @tsf64: The new 64bit TSF + * + * Sets the new TSF + */ +void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64) +{ + ATH5K_TRACE(ah->ah_sc); + + ath5k_hw_reg_write(ah, tsf64 & 0xffffffff, AR5K_TSF_L32); + ath5k_hw_reg_write(ah, (tsf64 >> 32) & 0xffffffff, AR5K_TSF_U32); +} + +/** + * ath5k_hw_reset_tsf - Force a TSF reset + * + * @ah: The &struct ath5k_hw + * + * Forces a TSF reset on PCU + */ +void ath5k_hw_reset_tsf(struct ath5k_hw *ah) +{ + u32 val; + + ATH5K_TRACE(ah->ah_sc); + + val = ath5k_hw_reg_read(ah, AR5K_BEACON) | AR5K_BEACON_RESET_TSF; + + /* + * Each write to the RESET_TSF bit toggles a hardware internal + * signal to reset TSF, but if left high it will cause a TSF reset + * on the next chip reset as well. Thus we always write the value + * twice to clear the signal. + */ + ath5k_hw_reg_write(ah, val, AR5K_BEACON); + ath5k_hw_reg_write(ah, val, AR5K_BEACON); +} + +/* + * Initialize beacon timers + */ +void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) +{ + u32 timer1, timer2, timer3; + + ATH5K_TRACE(ah->ah_sc); + /* + * Set the additional timers by mode + */ + switch (ah->ah_op_mode) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_STATION: + /* In STA mode timer1 is used as next wakeup + * timer and timer2 as next CFP duration start + * timer. Both in 1/8TUs. */ + /* TODO: PCF handling */ + if (ah->ah_version == AR5K_AR5210) { + timer1 = 0xffffffff; + timer2 = 0xffffffff; + } else { + timer1 = 0x0000ffff; + timer2 = 0x0007ffff; + } + /* Mark associated AP as PCF incapable for now */ + AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PCF); + break; + case NL80211_IFTYPE_ADHOC: + AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_ADHOC_BCN_ATIM); + default: + /* On non-STA modes timer1 is used as next DMA + * beacon alert (DBA) timer and timer2 as next + * software beacon alert. Both in 1/8TUs. */ + timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3; + timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3; + break; + } + + /* Timer3 marks the end of our ATIM window + * a zero length window is not allowed because + * we 'll get no beacons */ + timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1); + + /* + * Set the beacon register and enable all timers. + */ + /* When in AP mode zero timer0 to start TSF */ + if (ah->ah_op_mode == NL80211_IFTYPE_AP) + ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); + + ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); + ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1); + ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2); + ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3); + + /* Force a TSF reset if requested and enable beacons */ + if (interval & AR5K_BEACON_RESET_TSF) + ath5k_hw_reset_tsf(ah); + + ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD | + AR5K_BEACON_ENABLE), + AR5K_BEACON); + + /* Flush any pending BMISS interrupts on ISR by + * performing a clear-on-write operation on PISR + * register for the BMISS bit (writing a bit on + * ISR togles a reset for that bit and leaves + * the rest bits intact) */ + if (ah->ah_version == AR5K_AR5210) + ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_ISR); + else + ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_PISR); + + /* TODO: Set enchanced sleep registers on AR5212 + * based on vif->bss_conf params, until then + * disable power save reporting.*/ + AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PWR_SV); + +} + +#if 0 +/* + * Set beacon timers + */ +int ath5k_hw_set_beacon_timers(struct ath5k_hw *ah, + const struct ath5k_beacon_state *state) +{ + u32 cfp_period, next_cfp, dtim, interval, next_beacon; + + /* + * TODO: should be changed through *state + * review struct ath5k_beacon_state struct + * + * XXX: These are used for cfp period bellow, are they + * ok ? Is it O.K. for tsf here to be 0 or should we use + * get_tsf ? + */ + u32 dtim_count = 0; /* XXX */ + u32 cfp_count = 0; /* XXX */ + u32 tsf = 0; /* XXX */ + + ATH5K_TRACE(ah->ah_sc); + /* Return on an invalid beacon state */ + if (state->bs_interval < 1) + return -EINVAL; + + interval = state->bs_interval; + dtim = state->bs_dtim_period; + + /* + * PCF support? + */ + if (state->bs_cfp_period > 0) { + /* + * Enable PCF mode and set the CFP + * (Contention Free Period) and timer registers + */ + cfp_period = state->bs_cfp_period * state->bs_dtim_period * + state->bs_interval; + next_cfp = (cfp_count * state->bs_dtim_period + dtim_count) * + state->bs_interval; + + AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, + AR5K_STA_ID1_DEFAULT_ANTENNA | + AR5K_STA_ID1_PCF); + ath5k_hw_reg_write(ah, cfp_period, AR5K_CFP_PERIOD); + ath5k_hw_reg_write(ah, state->bs_cfp_max_duration, + AR5K_CFP_DUR); + ath5k_hw_reg_write(ah, (tsf + (next_cfp == 0 ? cfp_period : + next_cfp)) << 3, AR5K_TIMER2); + } else { + /* Disable PCF mode */ + AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, + AR5K_STA_ID1_DEFAULT_ANTENNA | + AR5K_STA_ID1_PCF); + } + + /* + * Enable the beacon timer register + */ + ath5k_hw_reg_write(ah, state->bs_next_beacon, AR5K_TIMER0); + + /* + * Start the beacon timers + */ + ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_BEACON) & + ~(AR5K_BEACON_PERIOD | AR5K_BEACON_TIM)) | + AR5K_REG_SM(state->bs_tim_offset ? state->bs_tim_offset + 4 : 0, + AR5K_BEACON_TIM) | AR5K_REG_SM(state->bs_interval, + AR5K_BEACON_PERIOD), AR5K_BEACON); + + /* + * Write new beacon miss threshold, if it appears to be valid + * XXX: Figure out right values for min <= bs_bmiss_threshold <= max + * and return if its not in range. We can test this by reading value and + * setting value to a largest value and seeing which values register. + */ + + AR5K_REG_WRITE_BITS(ah, AR5K_RSSI_THR, AR5K_RSSI_THR_BMISS, + state->bs_bmiss_threshold); + + /* + * Set sleep control register + * XXX: Didn't find this in 5210 code but since this register + * exists also in ar5k's 5210 headers i leave it as common code. + */ + AR5K_REG_WRITE_BITS(ah, AR5K_SLEEP_CTL, AR5K_SLEEP_CTL_SLDUR, + (state->bs_sleep_duration - 3) << 3); + + /* + * Set enhanced sleep registers on 5212 + */ + if (ah->ah_version == AR5K_AR5212) { + if (state->bs_sleep_duration > state->bs_interval && + roundup(state->bs_sleep_duration, interval) == + state->bs_sleep_duration) + interval = state->bs_sleep_duration; + + if (state->bs_sleep_duration > dtim && (dtim == 0 || + roundup(state->bs_sleep_duration, dtim) == + state->bs_sleep_duration)) + dtim = state->bs_sleep_duration; + + if (interval > dtim) + return -EINVAL; + + next_beacon = interval == dtim ? state->bs_next_dtim : + state->bs_next_beacon; + + ath5k_hw_reg_write(ah, + AR5K_REG_SM((state->bs_next_dtim - 3) << 3, + AR5K_SLEEP0_NEXT_DTIM) | + AR5K_REG_SM(10, AR5K_SLEEP0_CABTO) | + AR5K_SLEEP0_ENH_SLEEP_EN | + AR5K_SLEEP0_ASSUME_DTIM, AR5K_SLEEP0); + + ath5k_hw_reg_write(ah, AR5K_REG_SM((next_beacon - 3) << 3, + AR5K_SLEEP1_NEXT_TIM) | + AR5K_REG_SM(10, AR5K_SLEEP1_BEACON_TO), AR5K_SLEEP1); + + ath5k_hw_reg_write(ah, + AR5K_REG_SM(interval, AR5K_SLEEP2_TIM_PER) | + AR5K_REG_SM(dtim, AR5K_SLEEP2_DTIM_PER), AR5K_SLEEP2); + } + + return 0; +} + +/* + * Reset beacon timers + */ +void ath5k_hw_reset_beacon(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + /* + * Disable beacon timer + */ + ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); + + /* + * Disable some beacon register values + */ + AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, + AR5K_STA_ID1_DEFAULT_ANTENNA | AR5K_STA_ID1_PCF); + ath5k_hw_reg_write(ah, AR5K_BEACON_PERIOD, AR5K_BEACON); +} + +/* + * Wait for beacon queue to finish + */ +int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr) +{ + unsigned int i; + int ret; + + ATH5K_TRACE(ah->ah_sc); + + /* 5210 doesn't have QCU*/ + if (ah->ah_version == AR5K_AR5210) { + /* + * Wait for beaconn queue to finish by checking + * Control Register and Beacon Status Register. + */ + for (i = AR5K_TUNE_BEACON_INTERVAL / 2; i > 0; i--) { + if (!(ath5k_hw_reg_read(ah, AR5K_BSR) & AR5K_BSR_TXQ1F) + || + !(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_BSR_TXQ1F)) + break; + udelay(10); + } + + /* Timeout... */ + if (i <= 0) { + /* + * Re-schedule the beacon queue + */ + ath5k_hw_reg_write(ah, phys_addr, AR5K_NOQCU_TXDP1); + ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, + AR5K_BCR); + + return -EIO; + } + ret = 0; + } else { + /*5211/5212*/ + ret = ath5k_hw_register_timeout(ah, + AR5K_QUEUE_STATUS(AR5K_TX_QUEUE_ID_BEACON), + AR5K_QCU_STS_FRMPENDCNT, 0, false); + + if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, AR5K_TX_QUEUE_ID_BEACON)) + return -EIO; + } + + return ret; +} +#endif + + +/*********************\ +* Key table functions * +\*********************/ + +/* + * Reset a key entry on the table + */ +int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) +{ + unsigned int i, type; + u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; + + ATH5K_TRACE(ah->ah_sc); + AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + + type = ath5k_hw_reg_read(ah, AR5K_KEYTABLE_TYPE(entry)); + + for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) + ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i)); + + /* Reset associated MIC entry if TKIP + * is enabled located at offset (entry + 64) */ + if (type == AR5K_KEYTABLE_TYPE_TKIP) { + AR5K_ASSERT_ENTRY(micentry, AR5K_KEYTABLE_SIZE); + for (i = 0; i < AR5K_KEYCACHE_SIZE / 2 ; i++) + ath5k_hw_reg_write(ah, 0, + AR5K_KEYTABLE_OFF(micentry, i)); + } + + /* + * Set NULL encryption on AR5212+ + * + * Note: AR5K_KEYTABLE_TYPE -> AR5K_KEYTABLE_OFF(entry, 5) + * AR5K_KEYTABLE_TYPE_NULL -> 0x00000007 + * + * Note2: Windows driver (ndiswrapper) sets this to + * 0x00000714 instead of 0x00000007 + */ + if (ah->ah_version >= AR5K_AR5211) { + ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, + AR5K_KEYTABLE_TYPE(entry)); + + if (type == AR5K_KEYTABLE_TYPE_TKIP) { + ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, + AR5K_KEYTABLE_TYPE(micentry)); + } + } + + return 0; +} + +/* + * Check if a table entry is valid + */ +int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry) +{ + ATH5K_TRACE(ah->ah_sc); + AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + + /* Check the validation flag at the end of the entry */ + return ath5k_hw_reg_read(ah, AR5K_KEYTABLE_MAC1(entry)) & + AR5K_KEYTABLE_VALID; +} + +static +int ath5k_keycache_type(const struct ieee80211_key_conf *key) +{ + switch (key->alg) { + case ALG_TKIP: + return AR5K_KEYTABLE_TYPE_TKIP; + case ALG_CCMP: + return AR5K_KEYTABLE_TYPE_CCM; + case ALG_WEP: + if (key->keylen == WLAN_KEY_LEN_WEP40) + return AR5K_KEYTABLE_TYPE_40; + else if (key->keylen == WLAN_KEY_LEN_WEP104) + return AR5K_KEYTABLE_TYPE_104; + return -EINVAL; + default: + return -EINVAL; + } + return -EINVAL; +} + +/* + * Set a key entry on the table + */ +int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, + const struct ieee80211_key_conf *key, const u8 *mac) +{ + unsigned int i; + int keylen; + __le32 key_v[5] = {}; + __le32 key0 = 0, key1 = 0; + __le32 *rxmic, *txmic; + int keytype; + u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; + bool is_tkip; + const u8 *key_ptr; + + ATH5K_TRACE(ah->ah_sc); + + is_tkip = (key->alg == ALG_TKIP); + + /* + * key->keylen comes in from mac80211 in bytes. + * TKIP is 128 bit + 128 bit mic + */ + keylen = (is_tkip) ? (128 / 8) : key->keylen; + + if (entry > AR5K_KEYTABLE_SIZE || + (is_tkip && micentry > AR5K_KEYTABLE_SIZE)) + return -EOPNOTSUPP; + + if (unlikely(keylen > 16)) + return -EOPNOTSUPP; + + keytype = ath5k_keycache_type(key); + if (keytype < 0) + return keytype; + + /* + * each key block is 6 bytes wide, written as pairs of + * alternating 32 and 16 bit le values. + */ + key_ptr = key->key; + for (i = 0; keylen >= 6; keylen -= 6) { + memcpy(&key_v[i], key_ptr, 6); + i += 2; + key_ptr += 6; + } + if (keylen) + memcpy(&key_v[i], key_ptr, keylen); + + /* intentionally corrupt key until mic is installed */ + if (is_tkip) { + key0 = key_v[0] = ~key_v[0]; + key1 = key_v[1] = ~key_v[1]; + } + + for (i = 0; i < ARRAY_SIZE(key_v); i++) + ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), + AR5K_KEYTABLE_OFF(entry, i)); + + ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry)); + + if (is_tkip) { + /* Install rx/tx MIC */ + rxmic = (__le32 *) &key->key[16]; + txmic = (__le32 *) &key->key[24]; + + if (ah->ah_combined_mic) { + key_v[0] = rxmic[0]; + key_v[1] = cpu_to_le32(le32_to_cpu(txmic[0]) >> 16); + key_v[2] = rxmic[1]; + key_v[3] = cpu_to_le32(le32_to_cpu(txmic[0]) & 0xffff); + key_v[4] = txmic[1]; + } else { + key_v[0] = rxmic[0]; + key_v[1] = 0; + key_v[2] = rxmic[1]; + key_v[3] = 0; + key_v[4] = 0; + } + for (i = 0; i < ARRAY_SIZE(key_v); i++) + ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), + AR5K_KEYTABLE_OFF(micentry, i)); + + ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, + AR5K_KEYTABLE_TYPE(micentry)); + ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC0(micentry)); + ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC1(micentry)); + + /* restore first 2 words of key */ + ath5k_hw_reg_write(ah, le32_to_cpu(~key0), + AR5K_KEYTABLE_OFF(entry, 0)); + ath5k_hw_reg_write(ah, le32_to_cpu(~key1), + AR5K_KEYTABLE_OFF(entry, 1)); + } + + return ath5k_hw_set_key_lladdr(ah, entry, mac); +} + +int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac) +{ + u32 low_id, high_id; + + ATH5K_TRACE(ah->ah_sc); + /* Invalid entry (key table overflow) */ + AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + + /* MAC may be NULL if it's a broadcast key. In this case no need to + * to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */ + if (!mac) { + low_id = 0xffffffff; + high_id = 0xffff | AR5K_KEYTABLE_VALID; + } else { + low_id = AR5K_LOW_ID(mac); + high_id = AR5K_HIGH_ID(mac) | AR5K_KEYTABLE_VALID; + } + + ath5k_hw_reg_write(ah, low_id, AR5K_KEYTABLE_MAC0(entry)); + ath5k_hw_reg_write(ah, high_id, AR5K_KEYTABLE_MAC1(entry)); + + return 0; +} + diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c new file mode 100644 index 00000000000..a876ca8d69e --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -0,0 +1,3044 @@ +/* + * PHY functions + * + * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com> + * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org> + * + * Permission to use, copy, modify, and 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. + * + */ + +#define _ATH5K_PHY + +#include <linux/delay.h> + +#include "ath5k.h" +#include "reg.h" +#include "base.h" +#include "rfbuffer.h" +#include "rfgain.h" + +/* + * Used to modify RF Banks before writing them to AR5K_RF_BUFFER + */ +static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah, + const struct ath5k_rf_reg *rf_regs, + u32 val, u8 reg_id, bool set) +{ + const struct ath5k_rf_reg *rfreg = NULL; + u8 offset, bank, num_bits, col, position; + u16 entry; + u32 mask, data, last_bit, bits_shifted, first_bit; + u32 *rfb; + s32 bits_left; + int i; + + data = 0; + rfb = ah->ah_rf_banks; + + for (i = 0; i < ah->ah_rf_regs_count; i++) { + if (rf_regs[i].index == reg_id) { + rfreg = &rf_regs[i]; + break; + } + } + + if (rfb == NULL || rfreg == NULL) { + ATH5K_PRINTF("Rf register not found!\n"); + /* should not happen */ + return 0; + } + + bank = rfreg->bank; + num_bits = rfreg->field.len; + first_bit = rfreg->field.pos; + col = rfreg->field.col; + + /* first_bit is an offset from bank's + * start. Since we have all banks on + * the same array, we use this offset + * to mark each bank's start */ + offset = ah->ah_offset[bank]; + + /* Boundary check */ + if (!(col <= 3 && num_bits <= 32 && first_bit + num_bits <= 319)) { + ATH5K_PRINTF("invalid values at offset %u\n", offset); + return 0; + } + + entry = ((first_bit - 1) / 8) + offset; + position = (first_bit - 1) % 8; + + if (set) + data = ath5k_hw_bitswap(val, num_bits); + + for (bits_shifted = 0, bits_left = num_bits; bits_left > 0; + position = 0, entry++) { + + last_bit = (position + bits_left > 8) ? 8 : + position + bits_left; + + mask = (((1 << last_bit) - 1) ^ ((1 << position) - 1)) << + (col * 8); + + if (set) { + rfb[entry] &= ~mask; + rfb[entry] |= ((data << position) << (col * 8)) & mask; + data >>= (8 - position); + } else { + data |= (((rfb[entry] & mask) >> (col * 8)) >> position) + << bits_shifted; + bits_shifted += last_bit - position; + } + + bits_left -= 8 - position; + } + + data = set ? 1 : ath5k_hw_bitswap(data, num_bits); + + return data; +} + +/**********************\ +* RF Gain optimization * +\**********************/ + +/* + * This code is used to optimize rf gain on different environments + * (temprature mostly) based on feedback from a power detector. + * + * It's only used on RF5111 and RF5112, later RF chips seem to have + * auto adjustment on hw -notice they have a much smaller BANK 7 and + * no gain optimization ladder-. + * + * For more infos check out this patent doc + * http://www.freepatentsonline.com/7400691.html + * + * This paper describes power drops as seen on the receiver due to + * probe packets + * http://www.cnri.dit.ie/publications/ICT08%20-%20Practical%20Issues + * %20of%20Power%20Control.pdf + * + * And this is the MadWiFi bug entry related to the above + * http://madwifi-project.org/ticket/1659 + * with various measurements and diagrams + * + * TODO: Deal with power drops due to probes by setting an apropriate + * tx power on the probe packets ! Make this part of the calibration process. + */ + +/* Initialize ah_gain durring attach */ +int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah) +{ + /* Initialize the gain optimization values */ + switch (ah->ah_radio) { + case AR5K_RF5111: + ah->ah_gain.g_step_idx = rfgain_opt_5111.go_default; + ah->ah_gain.g_low = 20; + ah->ah_gain.g_high = 35; + ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; + break; + case AR5K_RF5112: + ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default; + ah->ah_gain.g_low = 20; + ah->ah_gain.g_high = 85; + ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Schedule a gain probe check on the next transmited packet. + * That means our next packet is going to be sent with lower + * tx power and a Peak to Average Power Detector (PAPD) will try + * to measure the gain. + * + * XXX: How about forcing a tx packet (bypassing PCU arbitrator etc) + * just after we enable the probe so that we don't mess with + * standard traffic ? Maybe it's time to use sw interrupts and + * a probe tasklet !!! + */ +static void ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah) +{ + + /* Skip if gain calibration is inactive or + * we already handle a probe request */ + if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE) + return; + + /* Send the packet with 2dB below max power as + * patent doc suggest */ + ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_ofdm - 4, + AR5K_PHY_PAPD_PROBE_TXPOWER) | + AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE); + + ah->ah_gain.g_state = AR5K_RFGAIN_READ_REQUESTED; + +} + +/* Calculate gain_F measurement correction + * based on the current step for RF5112 rev. 2 */ +static u32 ath5k_hw_rf_gainf_corr(struct ath5k_hw *ah) +{ + u32 mix, step; + u32 *rf; + const struct ath5k_gain_opt *go; + const struct ath5k_gain_opt_step *g_step; + const struct ath5k_rf_reg *rf_regs; + + /* Only RF5112 Rev. 2 supports it */ + if ((ah->ah_radio != AR5K_RF5112) || + (ah->ah_radio_5ghz_revision <= AR5K_SREV_RAD_5112A)) + return 0; + + go = &rfgain_opt_5112; + rf_regs = rf_regs_5112a; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a); + + g_step = &go->go_step[ah->ah_gain.g_step_idx]; + + if (ah->ah_rf_banks == NULL) + return 0; + + rf = ah->ah_rf_banks; + ah->ah_gain.g_f_corr = 0; + + /* No VGA (Variable Gain Amplifier) override, skip */ + if (ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR, false) != 1) + return 0; + + /* Mix gain stepping */ + step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXGAIN_STEP, false); + + /* Mix gain override */ + mix = g_step->gos_param[0]; + + switch (mix) { + case 3: + ah->ah_gain.g_f_corr = step * 2; + break; + case 2: + ah->ah_gain.g_f_corr = (step - 5) * 2; + break; + case 1: + ah->ah_gain.g_f_corr = step; + break; + default: + ah->ah_gain.g_f_corr = 0; + break; + } + + return ah->ah_gain.g_f_corr; +} + +/* Check if current gain_F measurement is in the range of our + * power detector windows. If we get a measurement outside range + * we know it's not accurate (detectors can't measure anything outside + * their detection window) so we must ignore it */ +static bool ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah) +{ + const struct ath5k_rf_reg *rf_regs; + u32 step, mix_ovr, level[4]; + u32 *rf; + + if (ah->ah_rf_banks == NULL) + return false; + + rf = ah->ah_rf_banks; + + if (ah->ah_radio == AR5K_RF5111) { + + rf_regs = rf_regs_5111; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111); + + step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_RFGAIN_STEP, + false); + + level[0] = 0; + level[1] = (step == 63) ? 50 : step + 4; + level[2] = (step != 63) ? 64 : level[0]; + level[3] = level[2] + 50 ; + + ah->ah_gain.g_high = level[3] - + (step == 63 ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5); + ah->ah_gain.g_low = level[0] + + (step == 63 ? AR5K_GAIN_DYN_ADJUST_LO_MARGIN : 0); + } else { + + rf_regs = rf_regs_5112; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112); + + mix_ovr = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR, + false); + + level[0] = level[2] = 0; + + if (mix_ovr == 1) { + level[1] = level[3] = 83; + } else { + level[1] = level[3] = 107; + ah->ah_gain.g_high = 55; + } + } + + return (ah->ah_gain.g_current >= level[0] && + ah->ah_gain.g_current <= level[1]) || + (ah->ah_gain.g_current >= level[2] && + ah->ah_gain.g_current <= level[3]); +} + +/* Perform gain_F adjustment by choosing the right set + * of parameters from rf gain optimization ladder */ +static s8 ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah) +{ + const struct ath5k_gain_opt *go; + const struct ath5k_gain_opt_step *g_step; + int ret = 0; + + switch (ah->ah_radio) { + case AR5K_RF5111: + go = &rfgain_opt_5111; + break; + case AR5K_RF5112: + go = &rfgain_opt_5112; + break; + default: + return 0; + } + + g_step = &go->go_step[ah->ah_gain.g_step_idx]; + + if (ah->ah_gain.g_current >= ah->ah_gain.g_high) { + + /* Reached maximum */ + if (ah->ah_gain.g_step_idx == 0) + return -1; + + for (ah->ah_gain.g_target = ah->ah_gain.g_current; + ah->ah_gain.g_target >= ah->ah_gain.g_high && + ah->ah_gain.g_step_idx > 0; + g_step = &go->go_step[ah->ah_gain.g_step_idx]) + ah->ah_gain.g_target -= 2 * + (go->go_step[--(ah->ah_gain.g_step_idx)].gos_gain - + g_step->gos_gain); + + ret = 1; + goto done; + } + + if (ah->ah_gain.g_current <= ah->ah_gain.g_low) { + + /* Reached minimum */ + if (ah->ah_gain.g_step_idx == (go->go_steps_count - 1)) + return -2; + + for (ah->ah_gain.g_target = ah->ah_gain.g_current; + ah->ah_gain.g_target <= ah->ah_gain.g_low && + ah->ah_gain.g_step_idx < go->go_steps_count-1; + g_step = &go->go_step[ah->ah_gain.g_step_idx]) + ah->ah_gain.g_target -= 2 * + (go->go_step[++ah->ah_gain.g_step_idx].gos_gain - + g_step->gos_gain); + + ret = 2; + goto done; + } + +done: + ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, + "ret %d, gain step %u, current gain %u, target gain %u\n", + ret, ah->ah_gain.g_step_idx, ah->ah_gain.g_current, + ah->ah_gain.g_target); + + return ret; +} + +/* Main callback for thermal rf gain calibration engine + * Check for a new gain reading and schedule an adjustment + * if needed. + * + * TODO: Use sw interrupt to schedule reset if gain_F needs + * adjustment */ +enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah) +{ + u32 data, type; + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + + ATH5K_TRACE(ah->ah_sc); + + if (ah->ah_rf_banks == NULL || + ah->ah_gain.g_state == AR5K_RFGAIN_INACTIVE) + return AR5K_RFGAIN_INACTIVE; + + /* No check requested, either engine is inactive + * or an adjustment is already requested */ + if (ah->ah_gain.g_state != AR5K_RFGAIN_READ_REQUESTED) + goto done; + + /* Read the PAPD (Peak to Average Power Detector) + * register */ + data = ath5k_hw_reg_read(ah, AR5K_PHY_PAPD_PROBE); + + /* No probe is scheduled, read gain_F measurement */ + if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) { + ah->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S; + type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE); + + /* If tx packet is CCK correct the gain_F measurement + * by cck ofdm gain delta */ + if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK) { + if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) + ah->ah_gain.g_current += + ee->ee_cck_ofdm_gain_delta; + else + ah->ah_gain.g_current += + AR5K_GAIN_CCK_PROBE_CORR; + } + + /* Further correct gain_F measurement for + * RF5112A radios */ + if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) { + ath5k_hw_rf_gainf_corr(ah); + ah->ah_gain.g_current = + ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ? + (ah->ah_gain.g_current-ah->ah_gain.g_f_corr) : + 0; + } + + /* Check if measurement is ok and if we need + * to adjust gain, schedule a gain adjustment, + * else switch back to the acive state */ + if (ath5k_hw_rf_check_gainf_readback(ah) && + AR5K_GAIN_CHECK_ADJUST(&ah->ah_gain) && + ath5k_hw_rf_gainf_adjust(ah)) { + ah->ah_gain.g_state = AR5K_RFGAIN_NEED_CHANGE; + } else { + ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; + } + } + +done: + return ah->ah_gain.g_state; +} + +/* Write initial rf gain table to set the RF sensitivity + * this one works on all RF chips and has nothing to do + * with gain_F calibration */ +int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq) +{ + const struct ath5k_ini_rfgain *ath5k_rfg; + unsigned int i, size; + + switch (ah->ah_radio) { + case AR5K_RF5111: + ath5k_rfg = rfgain_5111; + size = ARRAY_SIZE(rfgain_5111); + break; + case AR5K_RF5112: + ath5k_rfg = rfgain_5112; + size = ARRAY_SIZE(rfgain_5112); + break; + case AR5K_RF2413: + ath5k_rfg = rfgain_2413; + size = ARRAY_SIZE(rfgain_2413); + break; + case AR5K_RF2316: + ath5k_rfg = rfgain_2316; + size = ARRAY_SIZE(rfgain_2316); + break; + case AR5K_RF5413: + ath5k_rfg = rfgain_5413; + size = ARRAY_SIZE(rfgain_5413); + break; + case AR5K_RF2317: + case AR5K_RF2425: + ath5k_rfg = rfgain_2425; + size = ARRAY_SIZE(rfgain_2425); + break; + default: + return -EINVAL; + } + + switch (freq) { + case AR5K_INI_RFGAIN_2GHZ: + case AR5K_INI_RFGAIN_5GHZ: + break; + default: + return -EINVAL; + } + + for (i = 0; i < size; i++) { + AR5K_REG_WAIT(i); + ath5k_hw_reg_write(ah, ath5k_rfg[i].rfg_value[freq], + (u32)ath5k_rfg[i].rfg_register); + } + + return 0; +} + + + +/********************\ +* RF Registers setup * +\********************/ + + +/* + * Setup RF registers by writing rf buffer on hw + */ +int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, + unsigned int mode) +{ + const struct ath5k_rf_reg *rf_regs; + const struct ath5k_ini_rfbuffer *ini_rfb; + const struct ath5k_gain_opt *go = NULL; + const struct ath5k_gain_opt_step *g_step; + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u8 ee_mode = 0; + u32 *rfb; + int i, obdb = -1, bank = -1; + + switch (ah->ah_radio) { + case AR5K_RF5111: + rf_regs = rf_regs_5111; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111); + ini_rfb = rfb_5111; + ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5111); + go = &rfgain_opt_5111; + break; + case AR5K_RF5112: + if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) { + rf_regs = rf_regs_5112a; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a); + ini_rfb = rfb_5112a; + ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112a); + } else { + rf_regs = rf_regs_5112; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112); + ini_rfb = rfb_5112; + ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112); + } + go = &rfgain_opt_5112; + break; + case AR5K_RF2413: + rf_regs = rf_regs_2413; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2413); + ini_rfb = rfb_2413; + ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2413); + break; + case AR5K_RF2316: + rf_regs = rf_regs_2316; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2316); + ini_rfb = rfb_2316; + ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2316); + break; + case AR5K_RF5413: + rf_regs = rf_regs_5413; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5413); + ini_rfb = rfb_5413; + ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5413); + break; + case AR5K_RF2317: + rf_regs = rf_regs_2425; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425); + ini_rfb = rfb_2317; + ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2317); + break; + case AR5K_RF2425: + rf_regs = rf_regs_2425; + ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425); + if (ah->ah_mac_srev < AR5K_SREV_AR2417) { + ini_rfb = rfb_2425; + ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2425); + } else { + ini_rfb = rfb_2417; + ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2417); + } + break; + default: + return -EINVAL; + } + + /* If it's the first time we set rf buffer, allocate + * ah->ah_rf_banks based on ah->ah_rf_banks_size + * we set above */ + if (ah->ah_rf_banks == NULL) { + ah->ah_rf_banks = kmalloc(sizeof(u32) * ah->ah_rf_banks_size, + GFP_KERNEL); + if (ah->ah_rf_banks == NULL) { + ATH5K_ERR(ah->ah_sc, "out of memory\n"); + return -ENOMEM; + } + } + + /* Copy values to modify them */ + rfb = ah->ah_rf_banks; + + for (i = 0; i < ah->ah_rf_banks_size; i++) { + if (ini_rfb[i].rfb_bank >= AR5K_MAX_RF_BANKS) { + ATH5K_ERR(ah->ah_sc, "invalid bank\n"); + return -EINVAL; + } + + /* Bank changed, write down the offset */ + if (bank != ini_rfb[i].rfb_bank) { + bank = ini_rfb[i].rfb_bank; + ah->ah_offset[bank] = i; + } + + rfb[i] = ini_rfb[i].rfb_mode_data[mode]; + } + + /* Set Output and Driver bias current (OB/DB) */ + if (channel->hw_value & CHANNEL_2GHZ) { + + if (channel->hw_value & CHANNEL_CCK) + ee_mode = AR5K_EEPROM_MODE_11B; + else + ee_mode = AR5K_EEPROM_MODE_11G; + + /* For RF511X/RF211X combination we + * use b_OB and b_DB parameters stored + * in eeprom on ee->ee_ob[ee_mode][0] + * + * For all other chips we use OB/DB for 2Ghz + * stored in the b/g modal section just like + * 802.11a on ee->ee_ob[ee_mode][1] */ + if ((ah->ah_radio == AR5K_RF5111) || + (ah->ah_radio == AR5K_RF5112)) + obdb = 0; + else + obdb = 1; + + ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb], + AR5K_RF_OB_2GHZ, true); + + ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb], + AR5K_RF_DB_2GHZ, true); + + /* RF5111 always needs OB/DB for 5GHz, even if we use 2GHz */ + } else if ((channel->hw_value & CHANNEL_5GHZ) || + (ah->ah_radio == AR5K_RF5111)) { + + /* For 11a, Turbo and XR we need to choose + * OB/DB based on frequency range */ + ee_mode = AR5K_EEPROM_MODE_11A; + obdb = channel->center_freq >= 5725 ? 3 : + (channel->center_freq >= 5500 ? 2 : + (channel->center_freq >= 5260 ? 1 : + (channel->center_freq > 4000 ? 0 : -1))); + + if (obdb < 0) + return -EINVAL; + + ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb], + AR5K_RF_OB_5GHZ, true); + + ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb], + AR5K_RF_DB_5GHZ, true); + } + + g_step = &go->go_step[ah->ah_gain.g_step_idx]; + + /* Bank Modifications (chip-specific) */ + if (ah->ah_radio == AR5K_RF5111) { + + /* Set gain_F settings according to current step */ + if (channel->hw_value & CHANNEL_OFDM) { + + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL, + AR5K_PHY_FRAME_CTL_TX_CLIP, + g_step->gos_param[0]); + + ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1], + AR5K_RF_PWD_90, true); + + ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2], + AR5K_RF_PWD_84, true); + + ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3], + AR5K_RF_RFGAIN_SEL, true); + + /* We programmed gain_F parameters, switch back + * to active state */ + ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; + + } + + /* Bank 6/7 setup */ + + ath5k_hw_rfb_op(ah, rf_regs, !ee->ee_xpd[ee_mode], + AR5K_RF_PWD_XPD, true); + + ath5k_hw_rfb_op(ah, rf_regs, ee->ee_x_gain[ee_mode], + AR5K_RF_XPD_GAIN, true); + + ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode], + AR5K_RF_GAIN_I, true); + + ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode], + AR5K_RF_PLO_SEL, true); + + /* TODO: Half/quarter channel support */ + } + + if (ah->ah_radio == AR5K_RF5112) { + + /* Set gain_F settings according to current step */ + if (channel->hw_value & CHANNEL_OFDM) { + + ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[0], + AR5K_RF_MIXGAIN_OVR, true); + + ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1], + AR5K_RF_PWD_138, true); + + ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2], + AR5K_RF_PWD_137, true); + + ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3], + AR5K_RF_PWD_136, true); + + ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[4], + AR5K_RF_PWD_132, true); + + ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[5], + AR5K_RF_PWD_131, true); + + ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[6], + AR5K_RF_PWD_130, true); + + /* We programmed gain_F parameters, switch back + * to active state */ + ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; + } + + /* Bank 6/7 setup */ + + ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode], + AR5K_RF_XPD_SEL, true); + + if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) { + /* Rev. 1 supports only one xpd */ + ath5k_hw_rfb_op(ah, rf_regs, + ee->ee_x_gain[ee_mode], + AR5K_RF_XPD_GAIN, true); + + } else { + /* TODO: Set high and low gain bits */ + ath5k_hw_rfb_op(ah, rf_regs, + ee->ee_x_gain[ee_mode], + AR5K_RF_PD_GAIN_LO, true); + ath5k_hw_rfb_op(ah, rf_regs, + ee->ee_x_gain[ee_mode], + AR5K_RF_PD_GAIN_HI, true); + + /* Lower synth voltage on Rev 2 */ + ath5k_hw_rfb_op(ah, rf_regs, 2, + AR5K_RF_HIGH_VC_CP, true); + + ath5k_hw_rfb_op(ah, rf_regs, 2, + AR5K_RF_MID_VC_CP, true); + + ath5k_hw_rfb_op(ah, rf_regs, 2, + AR5K_RF_LOW_VC_CP, true); + + ath5k_hw_rfb_op(ah, rf_regs, 2, + AR5K_RF_PUSH_UP, true); + + /* Decrease power consumption on 5213+ BaseBand */ + if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) { + ath5k_hw_rfb_op(ah, rf_regs, 1, + AR5K_RF_PAD2GND, true); + + ath5k_hw_rfb_op(ah, rf_regs, 1, + AR5K_RF_XB2_LVL, true); + + ath5k_hw_rfb_op(ah, rf_regs, 1, + AR5K_RF_XB5_LVL, true); + + ath5k_hw_rfb_op(ah, rf_regs, 1, + AR5K_RF_PWD_167, true); + + ath5k_hw_rfb_op(ah, rf_regs, 1, + AR5K_RF_PWD_166, true); + } + } + + ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode], + AR5K_RF_GAIN_I, true); + + /* TODO: Half/quarter channel support */ + + } + + if (ah->ah_radio == AR5K_RF5413 && + channel->hw_value & CHANNEL_2GHZ) { + + ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_DERBY_CHAN_SEL_MODE, + true); + + /* Set optimum value for early revisions (on pci-e chips) */ + if (ah->ah_mac_srev >= AR5K_SREV_AR5424 && + ah->ah_mac_srev < AR5K_SREV_AR5413) + ath5k_hw_rfb_op(ah, rf_regs, ath5k_hw_bitswap(6, 3), + AR5K_RF_PWD_ICLOBUF_2G, true); + + } + + /* Write RF banks on hw */ + for (i = 0; i < ah->ah_rf_banks_size; i++) { + AR5K_REG_WAIT(i); + ath5k_hw_reg_write(ah, rfb[i], ini_rfb[i].rfb_ctrl_register); + } + + return 0; +} + + +/**************************\ + PHY/RF channel functions +\**************************/ + +/* + * Check if a channel is supported + */ +bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags) +{ + /* Check if the channel is in our supported range */ + if (flags & CHANNEL_2GHZ) { + if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) && + (freq <= ah->ah_capabilities.cap_range.range_2ghz_max)) + return true; + } else if (flags & CHANNEL_5GHZ) + if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) && + (freq <= ah->ah_capabilities.cap_range.range_5ghz_max)) + return true; + + return false; +} + +/* + * Convertion needed for RF5110 + */ +static u32 ath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel) +{ + u32 athchan; + + /* + * Convert IEEE channel/MHz to an internal channel value used + * by the AR5210 chipset. This has not been verified with + * newer chipsets like the AR5212A who have a completely + * different RF/PHY part. + */ + athchan = (ath5k_hw_bitswap( + (ieee80211_frequency_to_channel( + channel->center_freq) - 24) / 2, 5) + << 1) | (1 << 6) | 0x1; + return athchan; +} + +/* + * Set channel on RF5110 + */ +static int ath5k_hw_rf5110_channel(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + u32 data; + + /* + * Set the channel and wait + */ + data = ath5k_hw_rf5110_chan2athchan(channel); + ath5k_hw_reg_write(ah, data, AR5K_RF_BUFFER); + ath5k_hw_reg_write(ah, 0, AR5K_RF_BUFFER_CONTROL_0); + mdelay(1); + + return 0; +} + +/* + * Convertion needed for 5111 + */ +static int ath5k_hw_rf5111_chan2athchan(unsigned int ieee, + struct ath5k_athchan_2ghz *athchan) +{ + int channel; + + /* Cast this value to catch negative channel numbers (>= -19) */ + channel = (int)ieee; + + /* + * Map 2GHz IEEE channel to 5GHz Atheros channel + */ + if (channel <= 13) { + athchan->a2_athchan = 115 + channel; + athchan->a2_flags = 0x46; + } else if (channel == 14) { + athchan->a2_athchan = 124; + athchan->a2_flags = 0x44; + } else if (channel >= 15 && channel <= 26) { + athchan->a2_athchan = ((channel - 14) * 4) + 132; + athchan->a2_flags = 0x46; + } else + return -EINVAL; + + return 0; +} + +/* + * Set channel on 5111 + */ +static int ath5k_hw_rf5111_channel(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + struct ath5k_athchan_2ghz ath5k_channel_2ghz; + unsigned int ath5k_channel = + ieee80211_frequency_to_channel(channel->center_freq); + u32 data0, data1, clock; + int ret; + + /* + * Set the channel on the RF5111 radio + */ + data0 = data1 = 0; + + if (channel->hw_value & CHANNEL_2GHZ) { + /* Map 2GHz channel to 5GHz Atheros channel ID */ + ret = ath5k_hw_rf5111_chan2athchan( + ieee80211_frequency_to_channel(channel->center_freq), + &ath5k_channel_2ghz); + if (ret) + return ret; + + ath5k_channel = ath5k_channel_2ghz.a2_athchan; + data0 = ((ath5k_hw_bitswap(ath5k_channel_2ghz.a2_flags, 8) & 0xff) + << 5) | (1 << 4); + } + + if (ath5k_channel < 145 || !(ath5k_channel & 1)) { + clock = 1; + data1 = ((ath5k_hw_bitswap(ath5k_channel - 24, 8) & 0xff) << 2) | + (clock << 1) | (1 << 10) | 1; + } else { + clock = 0; + data1 = ((ath5k_hw_bitswap((ath5k_channel - 24) / 2, 8) & 0xff) + << 2) | (clock << 1) | (1 << 10) | 1; + } + + ath5k_hw_reg_write(ah, (data1 & 0xff) | ((data0 & 0xff) << 8), + AR5K_RF_BUFFER); + ath5k_hw_reg_write(ah, ((data1 >> 8) & 0xff) | (data0 & 0xff00), + AR5K_RF_BUFFER_CONTROL_3); + + return 0; +} + +/* + * Set channel on 5112 and newer + */ +static int ath5k_hw_rf5112_channel(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + u32 data, data0, data1, data2; + u16 c; + + data = data0 = data1 = data2 = 0; + c = channel->center_freq; + + if (c < 4800) { + if (!((c - 2224) % 5)) { + data0 = ((2 * (c - 704)) - 3040) / 10; + data1 = 1; + } else if (!((c - 2192) % 5)) { + data0 = ((2 * (c - 672)) - 3040) / 10; + data1 = 0; + } else + return -EINVAL; + + data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8); + } else if ((c - (c % 5)) != 2 || c > 5435) { + if (!(c % 20) && c >= 5120) { + data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); + data2 = ath5k_hw_bitswap(3, 2); + } else if (!(c % 10)) { + data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8); + data2 = ath5k_hw_bitswap(2, 2); + } else if (!(c % 5)) { + data0 = ath5k_hw_bitswap((c - 4800) / 5, 8); + data2 = ath5k_hw_bitswap(1, 2); + } else + return -EINVAL; + } else { + data0 = ath5k_hw_bitswap((10 * (c - 2) - 4800) / 25 + 1, 8); + data2 = ath5k_hw_bitswap(0, 2); + } + + data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001; + + ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER); + ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5); + + return 0; +} + +/* + * Set the channel on the RF2425 + */ +static int ath5k_hw_rf2425_channel(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + u32 data, data0, data2; + u16 c; + + data = data0 = data2 = 0; + c = channel->center_freq; + + if (c < 4800) { + data0 = ath5k_hw_bitswap((c - 2272), 8); + data2 = 0; + /* ? 5GHz ? */ + } else if ((c - (c % 5)) != 2 || c > 5435) { + if (!(c % 20) && c < 5120) + data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); + else if (!(c % 10)) + data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8); + else if (!(c % 5)) + data0 = ath5k_hw_bitswap((c - 4800) / 5, 8); + else + return -EINVAL; + data2 = ath5k_hw_bitswap(1, 2); + } else { + data0 = ath5k_hw_bitswap((10 * (c - 2) - 4800) / 25 + 1, 8); + data2 = ath5k_hw_bitswap(0, 2); + } + + data = (data0 << 4) | data2 << 2 | 0x1001; + + ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER); + ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5); + + return 0; +} + +/* + * Set a channel on the radio chip + */ +int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) +{ + int ret; + /* + * Check bounds supported by the PHY (we don't care about regultory + * restrictions at this point). Note: hw_value already has the band + * (CHANNEL_2GHZ, or CHANNEL_5GHZ) so we inform ath5k_channel_ok() + * of the band by that */ + if (!ath5k_channel_ok(ah, channel->center_freq, channel->hw_value)) { + ATH5K_ERR(ah->ah_sc, + "channel frequency (%u MHz) out of supported " + "band range\n", + channel->center_freq); + return -EINVAL; + } + + /* + * Set the channel and wait + */ + switch (ah->ah_radio) { + case AR5K_RF5110: + ret = ath5k_hw_rf5110_channel(ah, channel); + break; + case AR5K_RF5111: + ret = ath5k_hw_rf5111_channel(ah, channel); + break; + case AR5K_RF2425: + ret = ath5k_hw_rf2425_channel(ah, channel); + break; + default: + ret = ath5k_hw_rf5112_channel(ah, channel); + break; + } + + if (ret) + return ret; + + /* Set JAPAN setting for channel 14 */ + if (channel->center_freq == 2484) { + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL, + AR5K_PHY_CCKTXCTL_JAPAN); + } else { + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL, + AR5K_PHY_CCKTXCTL_WORLD); + } + + ah->ah_current_channel.center_freq = channel->center_freq; + ah->ah_current_channel.hw_value = channel->hw_value; + ah->ah_turbo = channel->hw_value == CHANNEL_T ? true : false; + + return 0; +} + +/*****************\ + PHY calibration +\*****************/ + +/** + * ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration + * + * @ah: struct ath5k_hw pointer we are operating on + * @freq: the channel frequency, just used for error logging + * + * This function performs a noise floor calibration of the PHY and waits for + * it to complete. Then the noise floor value is compared to some maximum + * noise floor we consider valid. + * + * Note that this is different from what the madwifi HAL does: it reads the + * noise floor and afterwards initiates the calibration. Since the noise floor + * calibration can take some time to finish, depending on the current channel + * use, that avoids the occasional timeout warnings we are seeing now. + * + * See the following link for an Atheros patent on noise floor calibration: + * http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL \ + * &p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=7245893.PN.&OS=PN/7 + * + * XXX: Since during noise floor calibration antennas are detached according to + * the patent, we should stop tx queues here. + */ +int +ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq) +{ + int ret; + unsigned int i; + s32 noise_floor; + + /* + * Enable noise floor calibration + */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_NF); + + ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_NF, 0, false); + if (ret) { + ATH5K_ERR(ah->ah_sc, + "noise floor calibration timeout (%uMHz)\n", freq); + return -EAGAIN; + } + + /* Wait until the noise floor is calibrated and read the value */ + for (i = 20; i > 0; i--) { + mdelay(1); + noise_floor = ath5k_hw_reg_read(ah, AR5K_PHY_NF); + noise_floor = AR5K_PHY_NF_RVAL(noise_floor); + if (noise_floor & AR5K_PHY_NF_ACTIVE) { + noise_floor = AR5K_PHY_NF_AVAL(noise_floor); + + if (noise_floor <= AR5K_TUNE_NOISE_FLOOR) + break; + } + } + + ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, + "noise floor %d\n", noise_floor); + + if (noise_floor > AR5K_TUNE_NOISE_FLOOR) { + ATH5K_ERR(ah->ah_sc, + "noise floor calibration failed (%uMHz)\n", freq); + return -EAGAIN; + } + + ah->ah_noise_floor = noise_floor; + + return 0; +} + +/* + * Perform a PHY calibration on RF5110 + * -Fix BPSK/QAM Constellation (I/Q correction) + * -Calculate Noise Floor + */ +static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + u32 phy_sig, phy_agc, phy_sat, beacon; + int ret; + + /* + * Disable beacons and RX/TX queues, wait + */ + AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210, + AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210); + beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210); + ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210); + + mdelay(2); + + /* + * Set the channel (with AGC turned off) + */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); + udelay(10); + ret = ath5k_hw_channel(ah, channel); + + /* + * Activate PHY and wait + */ + ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); + mdelay(1); + + AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); + + if (ret) + return ret; + + /* + * Calibrate the radio chip + */ + + /* Remember normal state */ + phy_sig = ath5k_hw_reg_read(ah, AR5K_PHY_SIG); + phy_agc = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCOARSE); + phy_sat = ath5k_hw_reg_read(ah, AR5K_PHY_ADCSAT); + + /* Update radio registers */ + ath5k_hw_reg_write(ah, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) | + AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG); + + ath5k_hw_reg_write(ah, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI | + AR5K_PHY_AGCCOARSE_LO)) | + AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) | + AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE); + + ath5k_hw_reg_write(ah, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT | + AR5K_PHY_ADCSAT_THR)) | + AR5K_REG_SM(2, AR5K_PHY_ADCSAT_ICNT) | + AR5K_REG_SM(12, AR5K_PHY_ADCSAT_THR), AR5K_PHY_ADCSAT); + + udelay(20); + + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); + udelay(10); + ath5k_hw_reg_write(ah, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG); + AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); + + mdelay(1); + + /* + * Enable calibration and wait until completion + */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL); + + ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_CAL, 0, false); + + /* Reset to normal state */ + ath5k_hw_reg_write(ah, phy_sig, AR5K_PHY_SIG); + ath5k_hw_reg_write(ah, phy_agc, AR5K_PHY_AGCCOARSE); + ath5k_hw_reg_write(ah, phy_sat, AR5K_PHY_ADCSAT); + + if (ret) { + ATH5K_ERR(ah->ah_sc, "calibration timeout (%uMHz)\n", + channel->center_freq); + return ret; + } + + ath5k_hw_noise_floor_calibration(ah, channel->center_freq); + + /* + * Re-enable RX/TX and beacons + */ + AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210, + AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210); + ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210); + + return 0; +} + +/* + * Perform a PHY calibration on RF5111/5112 and newer chips + */ +static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + u32 i_pwr, q_pwr; + s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd; + int i; + ATH5K_TRACE(ah->ah_sc); + + if (!ah->ah_calibration || + ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) + goto done; + + /* Calibration has finished, get the results and re-run */ + for (i = 0; i <= 10; i++) { + iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR); + i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I); + q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q); + } + + i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7; + q_coffd = q_pwr >> 7; + + /* No correction */ + if (i_coffd == 0 || q_coffd == 0) + goto done; + + i_coff = ((-iq_corr) / i_coffd) & 0x3f; + + /* Boundary check */ + if (i_coff > 31) + i_coff = 31; + if (i_coff < -32) + i_coff = -32; + + q_coff = (((s32)i_pwr / q_coffd) - 128) & 0x1f; + + /* Boundary check */ + if (q_coff > 15) + q_coff = 15; + if (q_coff < -16) + q_coff = -16; + + /* Commit new I/Q value */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE | + ((u32)q_coff) | ((u32)i_coff << AR5K_PHY_IQ_CORR_Q_I_COFF_S)); + + /* Re-enable calibration -if we don't we'll commit + * the same values again and again */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, + AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_RUN); + +done: + + /* TODO: Separate noise floor calibration from I/Q calibration + * since noise floor calibration interrupts rx path while I/Q + * calibration doesn't. We don't need to run noise floor calibration + * as often as I/Q calibration.*/ + ath5k_hw_noise_floor_calibration(ah, channel->center_freq); + + /* Initiate a gain_F calibration */ + ath5k_hw_request_rfgain_probe(ah); + + return 0; +} + +/* + * Perform a PHY calibration + */ +int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + int ret; + + if (ah->ah_radio == AR5K_RF5110) + ret = ath5k_hw_rf5110_calibrate(ah, channel); + else + ret = ath5k_hw_rf511x_calibrate(ah, channel); + + return ret; +} + +/***************************\ +* Spur mitigation functions * +\***************************/ + +bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + u8 refclk_freq; + + if ((ah->ah_radio == AR5K_RF5112) || + (ah->ah_radio == AR5K_RF5413) || + (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) + refclk_freq = 40; + else + refclk_freq = 32; + + if ((channel->center_freq % refclk_freq != 0) && + ((channel->center_freq % refclk_freq < 10) || + (channel->center_freq % refclk_freq > 22))) + return true; + else + return false; +} + +void +ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u32 mag_mask[4] = {0, 0, 0, 0}; + u32 pilot_mask[2] = {0, 0}; + /* Note: fbin values are scaled up by 2 */ + u16 spur_chan_fbin, chan_fbin, symbol_width, spur_detection_window; + s32 spur_delta_phase, spur_freq_sigma_delta; + s32 spur_offset, num_symbols_x16; + u8 num_symbol_offsets, i, freq_band; + + /* Convert current frequency to fbin value (the same way channels + * are stored on EEPROM, check out ath5k_eeprom_bin2freq) and scale + * up by 2 so we can compare it later */ + if (channel->hw_value & CHANNEL_2GHZ) { + chan_fbin = (channel->center_freq - 2300) * 10; + freq_band = AR5K_EEPROM_BAND_2GHZ; + } else { + chan_fbin = (channel->center_freq - 4900) * 10; + freq_band = AR5K_EEPROM_BAND_5GHZ; + } + + /* Check if any spur_chan_fbin from EEPROM is + * within our current channel's spur detection range */ + spur_chan_fbin = AR5K_EEPROM_NO_SPUR; + spur_detection_window = AR5K_SPUR_CHAN_WIDTH; + /* XXX: Half/Quarter channels ?*/ + if (channel->hw_value & CHANNEL_TURBO) + spur_detection_window *= 2; + + for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) { + spur_chan_fbin = ee->ee_spur_chans[i][freq_band]; + + /* Note: mask cleans AR5K_EEPROM_NO_SPUR flag + * so it's zero if we got nothing from EEPROM */ + if (spur_chan_fbin == AR5K_EEPROM_NO_SPUR) { + spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK; + break; + } + + if ((chan_fbin - spur_detection_window <= + (spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK)) && + (chan_fbin + spur_detection_window >= + (spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK))) { + spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK; + break; + } + } + + /* We need to enable spur filter for this channel */ + if (spur_chan_fbin) { + spur_offset = spur_chan_fbin - chan_fbin; + /* + * Calculate deltas: + * spur_freq_sigma_delta -> spur_offset / sample_freq << 21 + * spur_delta_phase -> spur_offset / chip_freq << 11 + * Note: Both values have 100KHz resolution + */ + /* XXX: Half/Quarter rate channels ? */ + switch (channel->hw_value) { + case CHANNEL_A: + /* Both sample_freq and chip_freq are 40MHz */ + spur_delta_phase = (spur_offset << 17) / 25; + spur_freq_sigma_delta = (spur_delta_phase >> 10); + symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz; + break; + case CHANNEL_G: + /* sample_freq -> 40MHz chip_freq -> 44MHz + * (for b compatibility) */ + spur_freq_sigma_delta = (spur_offset << 8) / 55; + spur_delta_phase = (spur_offset << 17) / 25; + symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz; + break; + case CHANNEL_T: + case CHANNEL_TG: + /* Both sample_freq and chip_freq are 80MHz */ + spur_delta_phase = (spur_offset << 16) / 25; + spur_freq_sigma_delta = (spur_delta_phase >> 10); + symbol_width = AR5K_SPUR_SYMBOL_WIDTH_TURBO_100Hz; + break; + default: + return; + } + + /* Calculate pilot and magnitude masks */ + + /* Scale up spur_offset by 1000 to switch to 100HZ resolution + * and divide by symbol_width to find how many symbols we have + * Note: number of symbols is scaled up by 16 */ + num_symbols_x16 = ((spur_offset * 1000) << 4) / symbol_width; + + /* Spur is on a symbol if num_symbols_x16 % 16 is zero */ + if (!(num_symbols_x16 & 0xF)) + /* _X_ */ + num_symbol_offsets = 3; + else + /* _xx_ */ + num_symbol_offsets = 4; + + for (i = 0; i < num_symbol_offsets; i++) { + + /* Calculate pilot mask */ + s32 curr_sym_off = + (num_symbols_x16 / 16) + i + 25; + + /* Pilot magnitude mask seems to be a way to + * declare the boundaries for our detection + * window or something, it's 2 for the middle + * value(s) where the symbol is expected to be + * and 1 on the boundary values */ + u8 plt_mag_map = + (i == 0 || i == (num_symbol_offsets - 1)) + ? 1 : 2; + + if (curr_sym_off >= 0 && curr_sym_off <= 32) { + if (curr_sym_off <= 25) + pilot_mask[0] |= 1 << curr_sym_off; + else if (curr_sym_off >= 27) + pilot_mask[0] |= 1 << (curr_sym_off - 1); + } else if (curr_sym_off >= 33 && curr_sym_off <= 52) + pilot_mask[1] |= 1 << (curr_sym_off - 33); + + /* Calculate magnitude mask (for viterbi decoder) */ + if (curr_sym_off >= -1 && curr_sym_off <= 14) + mag_mask[0] |= + plt_mag_map << (curr_sym_off + 1) * 2; + else if (curr_sym_off >= 15 && curr_sym_off <= 30) + mag_mask[1] |= + plt_mag_map << (curr_sym_off - 15) * 2; + else if (curr_sym_off >= 31 && curr_sym_off <= 46) + mag_mask[2] |= + plt_mag_map << (curr_sym_off - 31) * 2; + else if (curr_sym_off >= 46 && curr_sym_off <= 53) + mag_mask[3] |= + plt_mag_map << (curr_sym_off - 47) * 2; + + } + + /* Write settings on hw to enable spur filter */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, + AR5K_PHY_BIN_MASK_CTL_RATE, 0xff); + /* XXX: Self correlator also ? */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, + AR5K_PHY_IQ_PILOT_MASK_EN | + AR5K_PHY_IQ_CHAN_MASK_EN | + AR5K_PHY_IQ_SPUR_FILT_EN); + + /* Set delta phase and freq sigma delta */ + ath5k_hw_reg_write(ah, + AR5K_REG_SM(spur_delta_phase, + AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE) | + AR5K_REG_SM(spur_freq_sigma_delta, + AR5K_PHY_TIMING_11_SPUR_FREQ_SD) | + AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC, + AR5K_PHY_TIMING_11); + + /* Write pilot masks */ + ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_7); + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8, + AR5K_PHY_TIMING_8_PILOT_MASK_2, + pilot_mask[1]); + + ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_9); + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10, + AR5K_PHY_TIMING_10_PILOT_MASK_2, + pilot_mask[1]); + + /* Write magnitude masks */ + ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK_1); + ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK_2); + ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK_3); + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, + AR5K_PHY_BIN_MASK_CTL_MASK_4, + mag_mask[3]); + + ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK2_1); + ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK2_2); + ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK2_3); + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4, + AR5K_PHY_BIN_MASK2_4_MASK_4, + mag_mask[3]); + + } else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & + AR5K_PHY_IQ_SPUR_FILT_EN) { + /* Clean up spur mitigation settings and disable fliter */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, + AR5K_PHY_BIN_MASK_CTL_RATE, 0); + AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_IQ, + AR5K_PHY_IQ_PILOT_MASK_EN | + AR5K_PHY_IQ_CHAN_MASK_EN | + AR5K_PHY_IQ_SPUR_FILT_EN); + ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_11); + + /* Clear pilot masks */ + ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_7); + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8, + AR5K_PHY_TIMING_8_PILOT_MASK_2, + 0); + + ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_9); + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10, + AR5K_PHY_TIMING_10_PILOT_MASK_2, + 0); + + /* Clear magnitude masks */ + ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_1); + ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_2); + ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_3); + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, + AR5K_PHY_BIN_MASK_CTL_MASK_4, + 0); + + ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_1); + ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_2); + ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_3); + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4, + AR5K_PHY_BIN_MASK2_4_MASK_4, + 0); + } +} + +/********************\ + Misc PHY functions +\********************/ + +int ath5k_hw_phy_disable(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + /*Just a try M.F.*/ + ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); + + return 0; +} + +/* + * Get the PHY Chip revision + */ +u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan) +{ + unsigned int i; + u32 srev; + u16 ret; + + ATH5K_TRACE(ah->ah_sc); + + /* + * Set the radio chip access register + */ + switch (chan) { + case CHANNEL_2GHZ: + ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0)); + break; + case CHANNEL_5GHZ: + ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); + break; + default: + return 0; + } + + mdelay(2); + + /* ...wait until PHY is ready and read the selected radio revision */ + ath5k_hw_reg_write(ah, 0x00001c16, AR5K_PHY(0x34)); + + for (i = 0; i < 8; i++) + ath5k_hw_reg_write(ah, 0x00010000, AR5K_PHY(0x20)); + + if (ah->ah_version == AR5K_AR5210) { + srev = ath5k_hw_reg_read(ah, AR5K_PHY(256) >> 28) & 0xf; + ret = (u16)ath5k_hw_bitswap(srev, 4) + 1; + } else { + srev = (ath5k_hw_reg_read(ah, AR5K_PHY(0x100)) >> 24) & 0xff; + ret = (u16)ath5k_hw_bitswap(((srev & 0xf0) >> 4) | + ((srev & 0x0f) << 4), 8); + } + + /* Reset to the 5GHz mode */ + ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); + + return ret; +} + +/*****************\ +* Antenna control * +\*****************/ + +void /*TODO:Boundary check*/ +ath5k_hw_set_def_antenna(struct ath5k_hw *ah, u8 ant) +{ + ATH5K_TRACE(ah->ah_sc); + + if (ah->ah_version != AR5K_AR5210) + ath5k_hw_reg_write(ah, ant & 0x7, AR5K_DEFAULT_ANTENNA); +} + +unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + + if (ah->ah_version != AR5K_AR5210) + return ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA) & 0x7; + + return false; /*XXX: What do we return for 5210 ?*/ +} + +/* + * Enable/disable fast rx antenna diversity + */ +static void +ath5k_hw_set_fast_div(struct ath5k_hw *ah, u8 ee_mode, bool enable) +{ + switch (ee_mode) { + case AR5K_EEPROM_MODE_11G: + /* XXX: This is set to + * disabled on initvals !!! */ + case AR5K_EEPROM_MODE_11A: + if (enable) + AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_OFDM_DIV_DIS); + else + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_OFDM_DIV_DIS); + break; + case AR5K_EEPROM_MODE_11B: + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_OFDM_DIV_DIS); + break; + default: + return; + } + + if (enable) { + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART, + AR5K_PHY_RESTART_DIV_GC, 0xc); + + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV, + AR5K_PHY_FAST_ANT_DIV_EN); + } else { + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART, + AR5K_PHY_RESTART_DIV_GC, 0x8); + + AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV, + AR5K_PHY_FAST_ANT_DIV_EN); + } +} + +/* + * Set antenna operating mode + */ +void +ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode) +{ + struct ieee80211_channel *channel = &ah->ah_current_channel; + bool use_def_for_tx, update_def_on_tx, use_def_for_rts, fast_div; + bool use_def_for_sg; + u8 def_ant, tx_ant, ee_mode; + u32 sta_id1 = 0; + + def_ant = ah->ah_def_ant; + + ATH5K_TRACE(ah->ah_sc); + + switch (channel->hw_value & CHANNEL_MODES) { + case CHANNEL_A: + case CHANNEL_T: + case CHANNEL_XR: + ee_mode = AR5K_EEPROM_MODE_11A; + break; + case CHANNEL_G: + case CHANNEL_TG: + ee_mode = AR5K_EEPROM_MODE_11G; + break; + case CHANNEL_B: + ee_mode = AR5K_EEPROM_MODE_11B; + break; + default: + ATH5K_ERR(ah->ah_sc, + "invalid channel: %d\n", channel->center_freq); + return; + } + + switch (ant_mode) { + case AR5K_ANTMODE_DEFAULT: + tx_ant = 0; + use_def_for_tx = false; + update_def_on_tx = false; + use_def_for_rts = false; + use_def_for_sg = false; + fast_div = true; + break; + case AR5K_ANTMODE_FIXED_A: + def_ant = 1; + tx_ant = 0; + use_def_for_tx = true; + update_def_on_tx = false; + use_def_for_rts = true; + use_def_for_sg = true; + fast_div = false; + break; + case AR5K_ANTMODE_FIXED_B: + def_ant = 2; + tx_ant = 0; + use_def_for_tx = true; + update_def_on_tx = false; + use_def_for_rts = true; + use_def_for_sg = true; + fast_div = false; + break; + case AR5K_ANTMODE_SINGLE_AP: + def_ant = 1; /* updated on tx */ + tx_ant = 0; + use_def_for_tx = true; + update_def_on_tx = true; + use_def_for_rts = true; + use_def_for_sg = true; + fast_div = true; + break; + case AR5K_ANTMODE_SECTOR_AP: + tx_ant = 1; /* variable */ + use_def_for_tx = false; + update_def_on_tx = false; + use_def_for_rts = true; + use_def_for_sg = false; + fast_div = false; + break; + case AR5K_ANTMODE_SECTOR_STA: + tx_ant = 1; /* variable */ + use_def_for_tx = true; + update_def_on_tx = false; + use_def_for_rts = true; + use_def_for_sg = false; + fast_div = true; + break; + case AR5K_ANTMODE_DEBUG: + def_ant = 1; + tx_ant = 2; + use_def_for_tx = false; + update_def_on_tx = false; + use_def_for_rts = false; + use_def_for_sg = false; + fast_div = false; + break; + default: + return; + } + + ah->ah_tx_ant = tx_ant; + ah->ah_ant_mode = ant_mode; + + sta_id1 |= use_def_for_tx ? AR5K_STA_ID1_DEFAULT_ANTENNA : 0; + sta_id1 |= update_def_on_tx ? AR5K_STA_ID1_DESC_ANTENNA : 0; + sta_id1 |= use_def_for_rts ? AR5K_STA_ID1_RTS_DEF_ANTENNA : 0; + sta_id1 |= use_def_for_sg ? AR5K_STA_ID1_SELFGEN_DEF_ANT : 0; + + AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_ANTENNA_SETTINGS); + + if (sta_id1) + AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, sta_id1); + + /* Note: set diversity before default antenna + * because it won't work correctly */ + ath5k_hw_set_fast_div(ah, ee_mode, fast_div); + ath5k_hw_set_def_antenna(ah, def_ant); +} + + +/****************\ +* TX power setup * +\****************/ + +/* + * Helper functions + */ + +/* + * Do linear interpolation between two given (x, y) points + */ +static s16 +ath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right, + s16 y_left, s16 y_right) +{ + s16 ratio, result; + + /* Avoid divide by zero and skip interpolation + * if we have the same point */ + if ((x_left == x_right) || (y_left == y_right)) + return y_left; + + /* + * Since we use ints and not fps, we need to scale up in + * order to get a sane ratio value (or else we 'll eg. get + * always 1 instead of 1.25, 1.75 etc). We scale up by 100 + * to have some accuracy both for 0.5 and 0.25 steps. + */ + ratio = ((100 * y_right - 100 * y_left)/(x_right - x_left)); + + /* Now scale down to be in range */ + result = y_left + (ratio * (target - x_left) / 100); + + return result; +} + +/* + * Find vertical boundary (min pwr) for the linear PCDAC curve. + * + * Since we have the top of the curve and we draw the line below + * until we reach 1 (1 pcdac step) we need to know which point + * (x value) that is so that we don't go below y axis and have negative + * pcdac values when creating the curve, or fill the table with zeroes. + */ +static s16 +ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR, + const s16 *pwrL, const s16 *pwrR) +{ + s8 tmp; + s16 min_pwrL, min_pwrR; + s16 pwr_i; + + if (WARN_ON(stepL[0] == stepL[1] || stepR[0] == stepR[1])) + return 0; + + if (pwrL[0] == pwrL[1]) + min_pwrL = pwrL[0]; + else { + pwr_i = pwrL[0]; + do { + pwr_i--; + tmp = (s8) ath5k_get_interpolated_value(pwr_i, + pwrL[0], pwrL[1], + stepL[0], stepL[1]); + } while (tmp > 1); + + min_pwrL = pwr_i; + } + + if (pwrR[0] == pwrR[1]) + min_pwrR = pwrR[0]; + else { + pwr_i = pwrR[0]; + do { + pwr_i--; + tmp = (s8) ath5k_get_interpolated_value(pwr_i, + pwrR[0], pwrR[1], + stepR[0], stepR[1]); + } while (tmp > 1); + + min_pwrR = pwr_i; + } + + /* Keep the right boundary so that it works for both curves */ + return max(min_pwrL, min_pwrR); +} + +/* + * Interpolate (pwr,vpd) points to create a Power to PDADC or a + * Power to PCDAC curve. + * + * Each curve has power on x axis (in 0.5dB units) and PCDAC/PDADC + * steps (offsets) on y axis. Power can go up to 31.5dB and max + * PCDAC/PDADC step for each curve is 64 but we can write more than + * one curves on hw so we can go up to 128 (which is the max step we + * can write on the final table). + * + * We write y values (PCDAC/PDADC steps) on hw. + */ +static void +ath5k_create_power_curve(s16 pmin, s16 pmax, + const s16 *pwr, const u8 *vpd, + u8 num_points, + u8 *vpd_table, u8 type) +{ + u8 idx[2] = { 0, 1 }; + s16 pwr_i = 2*pmin; + int i; + + if (num_points < 2) + return; + + /* We want the whole line, so adjust boundaries + * to cover the entire power range. Note that + * power values are already 0.25dB so no need + * to multiply pwr_i by 2 */ + if (type == AR5K_PWRTABLE_LINEAR_PCDAC) { + pwr_i = pmin; + pmin = 0; + pmax = 63; + } + + /* Find surrounding turning points (TPs) + * and interpolate between them */ + for (i = 0; (i <= (u16) (pmax - pmin)) && + (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) { + + /* We passed the right TP, move to the next set of TPs + * if we pass the last TP, extrapolate above using the last + * two TPs for ratio */ + if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) { + idx[0]++; + idx[1]++; + } + + vpd_table[i] = (u8) ath5k_get_interpolated_value(pwr_i, + pwr[idx[0]], pwr[idx[1]], + vpd[idx[0]], vpd[idx[1]]); + + /* Increase by 0.5dB + * (0.25 dB units) */ + pwr_i += 2; + } +} + +/* + * Get the surrounding per-channel power calibration piers + * for a given frequency so that we can interpolate between + * them and come up with an apropriate dataset for our current + * channel. + */ +static void +ath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah, + struct ieee80211_channel *channel, + struct ath5k_chan_pcal_info **pcinfo_l, + struct ath5k_chan_pcal_info **pcinfo_r) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info *pcinfo; + u8 idx_l, idx_r; + u8 mode, max, i; + u32 target = channel->center_freq; + + idx_l = 0; + idx_r = 0; + + if (!(channel->hw_value & CHANNEL_OFDM)) { + pcinfo = ee->ee_pwr_cal_b; + mode = AR5K_EEPROM_MODE_11B; + } else if (channel->hw_value & CHANNEL_2GHZ) { + pcinfo = ee->ee_pwr_cal_g; + mode = AR5K_EEPROM_MODE_11G; + } else { + pcinfo = ee->ee_pwr_cal_a; + mode = AR5K_EEPROM_MODE_11A; + } + max = ee->ee_n_piers[mode] - 1; + + /* Frequency is below our calibrated + * range. Use the lowest power curve + * we have */ + if (target < pcinfo[0].freq) { + idx_l = idx_r = 0; + goto done; + } + + /* Frequency is above our calibrated + * range. Use the highest power curve + * we have */ + if (target > pcinfo[max].freq) { + idx_l = idx_r = max; + goto done; + } + + /* Frequency is inside our calibrated + * channel range. Pick the surrounding + * calibration piers so that we can + * interpolate */ + for (i = 0; i <= max; i++) { + + /* Frequency matches one of our calibration + * piers, no need to interpolate, just use + * that calibration pier */ + if (pcinfo[i].freq == target) { + idx_l = idx_r = i; + goto done; + } + + /* We found a calibration pier that's above + * frequency, use this pier and the previous + * one to interpolate */ + if (target < pcinfo[i].freq) { + idx_r = i; + idx_l = idx_r - 1; + goto done; + } + } + +done: + *pcinfo_l = &pcinfo[idx_l]; + *pcinfo_r = &pcinfo[idx_r]; + + return; +} + +/* + * Get the surrounding per-rate power calibration data + * for a given frequency and interpolate between power + * values to set max target power supported by hw for + * each rate. + */ +static void +ath5k_get_rate_pcal_data(struct ath5k_hw *ah, + struct ieee80211_channel *channel, + struct ath5k_rate_pcal_info *rates) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_rate_pcal_info *rpinfo; + u8 idx_l, idx_r; + u8 mode, max, i; + u32 target = channel->center_freq; + + idx_l = 0; + idx_r = 0; + + if (!(channel->hw_value & CHANNEL_OFDM)) { + rpinfo = ee->ee_rate_tpwr_b; + mode = AR5K_EEPROM_MODE_11B; + } else if (channel->hw_value & CHANNEL_2GHZ) { + rpinfo = ee->ee_rate_tpwr_g; + mode = AR5K_EEPROM_MODE_11G; + } else { + rpinfo = ee->ee_rate_tpwr_a; + mode = AR5K_EEPROM_MODE_11A; + } + max = ee->ee_rate_target_pwr_num[mode] - 1; + + /* Get the surrounding calibration + * piers - same as above */ + if (target < rpinfo[0].freq) { + idx_l = idx_r = 0; + goto done; + } + + if (target > rpinfo[max].freq) { + idx_l = idx_r = max; + goto done; + } + + for (i = 0; i <= max; i++) { + + if (rpinfo[i].freq == target) { + idx_l = idx_r = i; + goto done; + } + + if (target < rpinfo[i].freq) { + idx_r = i; + idx_l = idx_r - 1; + goto done; + } + } + +done: + /* Now interpolate power value, based on the frequency */ + rates->freq = target; + + rates->target_power_6to24 = + ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, + rpinfo[idx_r].freq, + rpinfo[idx_l].target_power_6to24, + rpinfo[idx_r].target_power_6to24); + + rates->target_power_36 = + ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, + rpinfo[idx_r].freq, + rpinfo[idx_l].target_power_36, + rpinfo[idx_r].target_power_36); + + rates->target_power_48 = + ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, + rpinfo[idx_r].freq, + rpinfo[idx_l].target_power_48, + rpinfo[idx_r].target_power_48); + + rates->target_power_54 = + ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, + rpinfo[idx_r].freq, + rpinfo[idx_l].target_power_54, + rpinfo[idx_r].target_power_54); +} + +/* + * Get the max edge power for this channel if + * we have such data from EEPROM's Conformance Test + * Limits (CTL), and limit max power if needed. + */ +static void +ath5k_get_max_ctl_power(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_edge_power *rep = ee->ee_ctl_pwr; + u8 *ctl_val = ee->ee_ctl; + s16 max_chan_pwr = ah->ah_txpower.txp_max_pwr / 4; + s16 edge_pwr = 0; + u8 rep_idx; + u8 i, ctl_mode; + u8 ctl_idx = 0xFF; + u32 target = channel->center_freq; + + ctl_mode = ath_regd_get_band_ctl(&ah->ah_regulatory, channel->band); + + switch (channel->hw_value & CHANNEL_MODES) { + case CHANNEL_A: + ctl_mode |= AR5K_CTL_11A; + break; + case CHANNEL_G: + ctl_mode |= AR5K_CTL_11G; + break; + case CHANNEL_B: + ctl_mode |= AR5K_CTL_11B; + break; + case CHANNEL_T: + ctl_mode |= AR5K_CTL_TURBO; + break; + case CHANNEL_TG: + ctl_mode |= AR5K_CTL_TURBOG; + break; + case CHANNEL_XR: + /* Fall through */ + default: + return; + } + + for (i = 0; i < ee->ee_ctls; i++) { + if (ctl_val[i] == ctl_mode) { + ctl_idx = i; + break; + } + } + + /* If we have a CTL dataset available grab it and find the + * edge power for our frequency */ + if (ctl_idx == 0xFF) + return; + + /* Edge powers are sorted by frequency from lower + * to higher. Each CTL corresponds to 8 edge power + * measurements. */ + rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES; + + /* Don't do boundaries check because we + * might have more that one bands defined + * for this mode */ + + /* Get the edge power that's closer to our + * frequency */ + for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) { + rep_idx += i; + if (target <= rep[rep_idx].freq) + edge_pwr = (s16) rep[rep_idx].edge; + } + + if (edge_pwr) + ah->ah_txpower.txp_max_pwr = 4*min(edge_pwr, max_chan_pwr); +} + + +/* + * Power to PCDAC table functions + */ + +/* + * Fill Power to PCDAC table on RF5111 + * + * No further processing is needed for RF5111, the only thing we have to + * do is fill the values below and above calibration range since eeprom data + * may not cover the entire PCDAC table. + */ +static void +ath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min, + s16 *table_max) +{ + u8 *pcdac_out = ah->ah_txpower.txp_pd_table; + u8 *pcdac_tmp = ah->ah_txpower.tmpL[0]; + u8 pcdac_0, pcdac_n, pcdac_i, pwr_idx, i; + s16 min_pwr, max_pwr; + + /* Get table boundaries */ + min_pwr = table_min[0]; + pcdac_0 = pcdac_tmp[0]; + + max_pwr = table_max[0]; + pcdac_n = pcdac_tmp[table_max[0] - table_min[0]]; + + /* Extrapolate below minimum using pcdac_0 */ + pcdac_i = 0; + for (i = 0; i < min_pwr; i++) + pcdac_out[pcdac_i++] = pcdac_0; + + /* Copy values from pcdac_tmp */ + pwr_idx = min_pwr; + for (i = 0 ; pwr_idx <= max_pwr && + pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) { + pcdac_out[pcdac_i++] = pcdac_tmp[i]; + pwr_idx++; + } + + /* Extrapolate above maximum */ + while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE) + pcdac_out[pcdac_i++] = pcdac_n; + +} + +/* + * Combine available XPD Curves and fill Linear Power to PCDAC table + * on RF5112 + * + * RFX112 can have up to 2 curves (one for low txpower range and one for + * higher txpower range). We need to put them both on pcdac_out and place + * them in the correct location. In case we only have one curve available + * just fit it on pcdac_out (it's supposed to cover the entire range of + * available pwr levels since it's always the higher power curve). Extrapolate + * below and above final table if needed. + */ +static void +ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min, + s16 *table_max, u8 pdcurves) +{ + u8 *pcdac_out = ah->ah_txpower.txp_pd_table; + u8 *pcdac_low_pwr; + u8 *pcdac_high_pwr; + u8 *pcdac_tmp; + u8 pwr; + s16 max_pwr_idx; + s16 min_pwr_idx; + s16 mid_pwr_idx = 0; + /* Edge flag turs on the 7nth bit on the PCDAC + * to delcare the higher power curve (force values + * to be greater than 64). If we only have one curve + * we don't need to set this, if we have 2 curves and + * fill the table backwards this can also be used to + * switch from higher power curve to lower power curve */ + u8 edge_flag; + int i; + + /* When we have only one curve available + * that's the higher power curve. If we have + * two curves the first is the high power curve + * and the next is the low power curve. */ + if (pdcurves > 1) { + pcdac_low_pwr = ah->ah_txpower.tmpL[1]; + pcdac_high_pwr = ah->ah_txpower.tmpL[0]; + mid_pwr_idx = table_max[1] - table_min[1] - 1; + max_pwr_idx = (table_max[0] - table_min[0]) / 2; + + /* If table size goes beyond 31.5dB, keep the + * upper 31.5dB range when setting tx power. + * Note: 126 = 31.5 dB in quarter dB steps */ + if (table_max[0] - table_min[1] > 126) + min_pwr_idx = table_max[0] - 126; + else + min_pwr_idx = table_min[1]; + + /* Since we fill table backwards + * start from high power curve */ + pcdac_tmp = pcdac_high_pwr; + + edge_flag = 0x40; +#if 0 + /* If both min and max power limits are in lower + * power curve's range, only use the low power curve. + * TODO: min/max levels are related to target + * power values requested from driver/user + * XXX: Is this really needed ? */ + if (min_pwr < table_max[1] && + max_pwr < table_max[1]) { + edge_flag = 0; + pcdac_tmp = pcdac_low_pwr; + max_pwr_idx = (table_max[1] - table_min[1])/2; + } +#endif + } else { + pcdac_low_pwr = ah->ah_txpower.tmpL[1]; /* Zeroed */ + pcdac_high_pwr = ah->ah_txpower.tmpL[0]; + min_pwr_idx = table_min[0]; + max_pwr_idx = (table_max[0] - table_min[0]) / 2; + pcdac_tmp = pcdac_high_pwr; + edge_flag = 0; + } + + /* This is used when setting tx power*/ + ah->ah_txpower.txp_min_idx = min_pwr_idx/2; + + /* Fill Power to PCDAC table backwards */ + pwr = max_pwr_idx; + for (i = 63; i >= 0; i--) { + /* Entering lower power range, reset + * edge flag and set pcdac_tmp to lower + * power curve.*/ + if (edge_flag == 0x40 && + (2*pwr <= (table_max[1] - table_min[0]) || pwr == 0)) { + edge_flag = 0x00; + pcdac_tmp = pcdac_low_pwr; + pwr = mid_pwr_idx/2; + } + + /* Don't go below 1, extrapolate below if we have + * already swithced to the lower power curve -or + * we only have one curve and edge_flag is zero + * anyway */ + if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) { + while (i >= 0) { + pcdac_out[i] = pcdac_out[i + 1]; + i--; + } + break; + } + + pcdac_out[i] = pcdac_tmp[pwr] | edge_flag; + + /* Extrapolate above if pcdac is greater than + * 126 -this can happen because we OR pcdac_out + * value with edge_flag on high power curve */ + if (pcdac_out[i] > 126) + pcdac_out[i] = 126; + + /* Decrease by a 0.5dB step */ + pwr--; + } +} + +/* Write PCDAC values on hw */ +static void +ath5k_setup_pcdac_table(struct ath5k_hw *ah) +{ + u8 *pcdac_out = ah->ah_txpower.txp_pd_table; + int i; + + /* + * Write TX power values + */ + for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) { + ath5k_hw_reg_write(ah, + (((pcdac_out[2*i + 0] << 8 | 0xff) & 0xffff) << 0) | + (((pcdac_out[2*i + 1] << 8 | 0xff) & 0xffff) << 16), + AR5K_PHY_PCDAC_TXPOWER(i)); + } +} + + +/* + * Power to PDADC table functions + */ + +/* + * Set the gain boundaries and create final Power to PDADC table + * + * We can have up to 4 pd curves, we need to do a simmilar process + * as we do for RF5112. This time we don't have an edge_flag but we + * set the gain boundaries on a separate register. + */ +static void +ath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah, + s16 *pwr_min, s16 *pwr_max, u8 pdcurves) +{ + u8 gain_boundaries[AR5K_EEPROM_N_PD_GAINS]; + u8 *pdadc_out = ah->ah_txpower.txp_pd_table; + u8 *pdadc_tmp; + s16 pdadc_0; + u8 pdadc_i, pdadc_n, pwr_step, pdg, max_idx, table_size; + u8 pd_gain_overlap; + + /* Note: Register value is initialized on initvals + * there is no feedback from hw. + * XXX: What about pd_gain_overlap from EEPROM ? */ + pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) & + AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP; + + /* Create final PDADC table */ + for (pdg = 0, pdadc_i = 0; pdg < pdcurves; pdg++) { + pdadc_tmp = ah->ah_txpower.tmpL[pdg]; + + if (pdg == pdcurves - 1) + /* 2 dB boundary stretch for last + * (higher power) curve */ + gain_boundaries[pdg] = pwr_max[pdg] + 4; + else + /* Set gain boundary in the middle + * between this curve and the next one */ + gain_boundaries[pdg] = + (pwr_max[pdg] + pwr_min[pdg + 1]) / 2; + + /* Sanity check in case our 2 db stretch got out of + * range. */ + if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER) + gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER; + + /* For the first curve (lower power) + * start from 0 dB */ + if (pdg == 0) + pdadc_0 = 0; + else + /* For the other curves use the gain overlap */ + pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) - + pd_gain_overlap; + + /* Force each power step to be at least 0.5 dB */ + if ((pdadc_tmp[1] - pdadc_tmp[0]) > 1) + pwr_step = pdadc_tmp[1] - pdadc_tmp[0]; + else + pwr_step = 1; + + /* If pdadc_0 is negative, we need to extrapolate + * below this pdgain by a number of pwr_steps */ + while ((pdadc_0 < 0) && (pdadc_i < 128)) { + s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step; + pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp; + pdadc_0++; + } + + /* Set last pwr level, using gain boundaries */ + pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg]; + /* Limit it to be inside pwr range */ + table_size = pwr_max[pdg] - pwr_min[pdg]; + max_idx = (pdadc_n < table_size) ? pdadc_n : table_size; + + /* Fill pdadc_out table */ + while (pdadc_0 < max_idx) + pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++]; + + /* Need to extrapolate above this pdgain? */ + if (pdadc_n <= max_idx) + continue; + + /* Force each power step to be at least 0.5 dB */ + if ((pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]) > 1) + pwr_step = pdadc_tmp[table_size - 1] - + pdadc_tmp[table_size - 2]; + else + pwr_step = 1; + + /* Extrapolate above */ + while ((pdadc_0 < (s16) pdadc_n) && + (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2)) { + s16 tmp = pdadc_tmp[table_size - 1] + + (pdadc_0 - max_idx) * pwr_step; + pdadc_out[pdadc_i++] = (tmp > 127) ? 127 : (u8) tmp; + pdadc_0++; + } + } + + while (pdg < AR5K_EEPROM_N_PD_GAINS) { + gain_boundaries[pdg] = gain_boundaries[pdg - 1]; + pdg++; + } + + while (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2) { + pdadc_out[pdadc_i] = pdadc_out[pdadc_i - 1]; + pdadc_i++; + } + + /* Set gain boundaries */ + ath5k_hw_reg_write(ah, + AR5K_REG_SM(pd_gain_overlap, + AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) | + AR5K_REG_SM(gain_boundaries[0], + AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) | + AR5K_REG_SM(gain_boundaries[1], + AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) | + AR5K_REG_SM(gain_boundaries[2], + AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) | + AR5K_REG_SM(gain_boundaries[3], + AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4), + AR5K_PHY_TPC_RG5); + + /* Used for setting rate power table */ + ah->ah_txpower.txp_min_idx = pwr_min[0]; + +} + +/* Write PDADC values on hw */ +static void +ath5k_setup_pwr_to_pdadc_table(struct ath5k_hw *ah, + u8 pdcurves, u8 *pdg_to_idx) +{ + u8 *pdadc_out = ah->ah_txpower.txp_pd_table; + u32 reg; + u8 i; + + /* Select the right pdgain curves */ + + /* Clear current settings */ + reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1); + reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 | + AR5K_PHY_TPC_RG1_PDGAIN_2 | + AR5K_PHY_TPC_RG1_PDGAIN_3 | + AR5K_PHY_TPC_RG1_NUM_PD_GAIN); + + /* + * Use pd_gains curve from eeprom + * + * This overrides the default setting from initvals + * in case some vendors (e.g. Zcomax) don't use the default + * curves. If we don't honor their settings we 'll get a + * 5dB (1 * gain overlap ?) drop. + */ + reg |= AR5K_REG_SM(pdcurves, AR5K_PHY_TPC_RG1_NUM_PD_GAIN); + + switch (pdcurves) { + case 3: + reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3); + /* Fall through */ + case 2: + reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2); + /* Fall through */ + case 1: + reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1); + break; + } + ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1); + + /* + * Write TX power values + */ + for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) { + ath5k_hw_reg_write(ah, + ((pdadc_out[4*i + 0] & 0xff) << 0) | + ((pdadc_out[4*i + 1] & 0xff) << 8) | + ((pdadc_out[4*i + 2] & 0xff) << 16) | + ((pdadc_out[4*i + 3] & 0xff) << 24), + AR5K_PHY_PDADC_TXPOWER(i)); + } +} + + +/* + * Common code for PCDAC/PDADC tables + */ + +/* + * This is the main function that uses all of the above + * to set PCDAC/PDADC table on hw for the current channel. + * This table is used for tx power calibration on the basband, + * without it we get weird tx power levels and in some cases + * distorted spectral mask + */ +static int +ath5k_setup_channel_powertable(struct ath5k_hw *ah, + struct ieee80211_channel *channel, + u8 ee_mode, u8 type) +{ + struct ath5k_pdgain_info *pdg_L, *pdg_R; + struct ath5k_chan_pcal_info *pcinfo_L; + struct ath5k_chan_pcal_info *pcinfo_R; + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode]; + s16 table_min[AR5K_EEPROM_N_PD_GAINS]; + s16 table_max[AR5K_EEPROM_N_PD_GAINS]; + u8 *tmpL; + u8 *tmpR; + u32 target = channel->center_freq; + int pdg, i; + + /* Get surounding freq piers for this channel */ + ath5k_get_chan_pcal_surrounding_piers(ah, channel, + &pcinfo_L, + &pcinfo_R); + + /* Loop over pd gain curves on + * surounding freq piers by index */ + for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) { + + /* Fill curves in reverse order + * from lower power (max gain) + * to higher power. Use curve -> idx + * backmaping we did on eeprom init */ + u8 idx = pdg_curve_to_idx[pdg]; + + /* Grab the needed curves by index */ + pdg_L = &pcinfo_L->pd_curves[idx]; + pdg_R = &pcinfo_R->pd_curves[idx]; + + /* Initialize the temp tables */ + tmpL = ah->ah_txpower.tmpL[pdg]; + tmpR = ah->ah_txpower.tmpR[pdg]; + + /* Set curve's x boundaries and create + * curves so that they cover the same + * range (if we don't do that one table + * will have values on some range and the + * other one won't have any so interpolation + * will fail) */ + table_min[pdg] = min(pdg_L->pd_pwr[0], + pdg_R->pd_pwr[0]) / 2; + + table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1], + pdg_R->pd_pwr[pdg_R->pd_points - 1]) / 2; + + /* Now create the curves on surrounding channels + * and interpolate if needed to get the final + * curve for this gain on this channel */ + switch (type) { + case AR5K_PWRTABLE_LINEAR_PCDAC: + /* Override min/max so that we don't loose + * accuracy (don't divide by 2) */ + table_min[pdg] = min(pdg_L->pd_pwr[0], + pdg_R->pd_pwr[0]); + + table_max[pdg] = + max(pdg_L->pd_pwr[pdg_L->pd_points - 1], + pdg_R->pd_pwr[pdg_R->pd_points - 1]); + + /* Override minimum so that we don't get + * out of bounds while extrapolating + * below. Don't do this when we have 2 + * curves and we are on the high power curve + * because table_min is ok in this case */ + if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) { + + table_min[pdg] = + ath5k_get_linear_pcdac_min(pdg_L->pd_step, + pdg_R->pd_step, + pdg_L->pd_pwr, + pdg_R->pd_pwr); + + /* Don't go too low because we will + * miss the upper part of the curve. + * Note: 126 = 31.5dB (max power supported) + * in 0.25dB units */ + if (table_max[pdg] - table_min[pdg] > 126) + table_min[pdg] = table_max[pdg] - 126; + } + + /* Fall through */ + case AR5K_PWRTABLE_PWR_TO_PCDAC: + case AR5K_PWRTABLE_PWR_TO_PDADC: + + ath5k_create_power_curve(table_min[pdg], + table_max[pdg], + pdg_L->pd_pwr, + pdg_L->pd_step, + pdg_L->pd_points, tmpL, type); + + /* We are in a calibration + * pier, no need to interpolate + * between freq piers */ + if (pcinfo_L == pcinfo_R) + continue; + + ath5k_create_power_curve(table_min[pdg], + table_max[pdg], + pdg_R->pd_pwr, + pdg_R->pd_step, + pdg_R->pd_points, tmpR, type); + break; + default: + return -EINVAL; + } + + /* Interpolate between curves + * of surounding freq piers to + * get the final curve for this + * pd gain. Re-use tmpL for interpolation + * output */ + for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) && + (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) { + tmpL[i] = (u8) ath5k_get_interpolated_value(target, + (s16) pcinfo_L->freq, + (s16) pcinfo_R->freq, + (s16) tmpL[i], + (s16) tmpR[i]); + } + } + + /* Now we have a set of curves for this + * channel on tmpL (x range is table_max - table_min + * and y values are tmpL[pdg][]) sorted in the same + * order as EEPROM (because we've used the backmaping). + * So for RF5112 it's from higher power to lower power + * and for RF2413 it's from lower power to higher power. + * For RF5111 we only have one curve. */ + + /* Fill min and max power levels for this + * channel by interpolating the values on + * surounding channels to complete the dataset */ + ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target, + (s16) pcinfo_L->freq, + (s16) pcinfo_R->freq, + pcinfo_L->min_pwr, pcinfo_R->min_pwr); + + ah->ah_txpower.txp_max_pwr = ath5k_get_interpolated_value(target, + (s16) pcinfo_L->freq, + (s16) pcinfo_R->freq, + pcinfo_L->max_pwr, pcinfo_R->max_pwr); + + /* We are ready to go, fill PCDAC/PDADC + * table and write settings on hardware */ + switch (type) { + case AR5K_PWRTABLE_LINEAR_PCDAC: + /* For RF5112 we can have one or two curves + * and each curve covers a certain power lvl + * range so we need to do some more processing */ + ath5k_combine_linear_pcdac_curves(ah, table_min, table_max, + ee->ee_pd_gains[ee_mode]); + + /* Set txp.offset so that we can + * match max power value with max + * table index */ + ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2); + + /* Write settings on hw */ + ath5k_setup_pcdac_table(ah); + break; + case AR5K_PWRTABLE_PWR_TO_PCDAC: + /* We are done for RF5111 since it has only + * one curve, just fit the curve on the table */ + ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max); + + /* No rate powertable adjustment for RF5111 */ + ah->ah_txpower.txp_min_idx = 0; + ah->ah_txpower.txp_offset = 0; + + /* Write settings on hw */ + ath5k_setup_pcdac_table(ah); + break; + case AR5K_PWRTABLE_PWR_TO_PDADC: + /* Set PDADC boundaries and fill + * final PDADC table */ + ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max, + ee->ee_pd_gains[ee_mode]); + + /* Write settings on hw */ + ath5k_setup_pwr_to_pdadc_table(ah, pdg, pdg_curve_to_idx); + + /* Set txp.offset, note that table_min + * can be negative */ + ah->ah_txpower.txp_offset = table_min[0]; + break; + default: + return -EINVAL; + } + + return 0; +} + + +/* + * Per-rate tx power setting + * + * This is the code that sets the desired tx power (below + * maximum) on hw for each rate (we also have TPC that sets + * power per packet). We do that by providing an index on the + * PCDAC/PDADC table we set up. + */ + +/* + * Set rate power table + * + * For now we only limit txpower based on maximum tx power + * supported by hw (what's inside rate_info). We need to limit + * this even more, based on regulatory domain etc. + * + * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps) + * and is indexed as follows: + * rates[0] - rates[7] -> OFDM rates + * rates[8] - rates[14] -> CCK rates + * rates[15] -> XR rates (they all have the same power) + */ +static void +ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr, + struct ath5k_rate_pcal_info *rate_info, + u8 ee_mode) +{ + unsigned int i; + u16 *rates; + + /* max_pwr is power level we got from driver/user in 0.5dB + * units, switch to 0.25dB units so we can compare */ + max_pwr *= 2; + max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2; + + /* apply rate limits */ + rates = ah->ah_txpower.txp_rates_power_table; + + /* OFDM rates 6 to 24Mb/s */ + for (i = 0; i < 5; i++) + rates[i] = min(max_pwr, rate_info->target_power_6to24); + + /* Rest OFDM rates */ + rates[5] = min(rates[0], rate_info->target_power_36); + rates[6] = min(rates[0], rate_info->target_power_48); + rates[7] = min(rates[0], rate_info->target_power_54); + + /* CCK rates */ + /* 1L */ + rates[8] = min(rates[0], rate_info->target_power_6to24); + /* 2L */ + rates[9] = min(rates[0], rate_info->target_power_36); + /* 2S */ + rates[10] = min(rates[0], rate_info->target_power_36); + /* 5L */ + rates[11] = min(rates[0], rate_info->target_power_48); + /* 5S */ + rates[12] = min(rates[0], rate_info->target_power_48); + /* 11L */ + rates[13] = min(rates[0], rate_info->target_power_54); + /* 11S */ + rates[14] = min(rates[0], rate_info->target_power_54); + + /* XR rates */ + rates[15] = min(rates[0], rate_info->target_power_6to24); + + /* CCK rates have different peak to average ratio + * so we have to tweak their power so that gainf + * correction works ok. For this we use OFDM to + * CCK delta from eeprom */ + if ((ee_mode == AR5K_EEPROM_MODE_11G) && + (ah->ah_phy_revision < AR5K_SREV_PHY_5212A)) + for (i = 8; i <= 15; i++) + rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta; + + /* Now that we have all rates setup use table offset to + * match the power range set by user with the power indices + * on PCDAC/PDADC table */ + for (i = 0; i < 16; i++) { + rates[i] += ah->ah_txpower.txp_offset; + /* Don't get out of bounds */ + if (rates[i] > 63) + rates[i] = 63; + } + + /* Min/max in 0.25dB units */ + ah->ah_txpower.txp_min_pwr = 2 * rates[7]; + ah->ah_txpower.txp_max_pwr = 2 * rates[0]; + ah->ah_txpower.txp_ofdm = rates[7]; +} + + +/* + * Set transmition power + */ +int +ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, + u8 ee_mode, u8 txpower) +{ + struct ath5k_rate_pcal_info rate_info; + u8 type; + int ret; + + ATH5K_TRACE(ah->ah_sc); + if (txpower > AR5K_TUNE_MAX_TXPOWER) { + ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower); + return -EINVAL; + } + if (txpower == 0) + txpower = AR5K_TUNE_DEFAULT_TXPOWER; + + /* Reset TX power values */ + memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower)); + ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; + ah->ah_txpower.txp_min_pwr = 0; + ah->ah_txpower.txp_max_pwr = AR5K_TUNE_MAX_TXPOWER; + + /* Initialize TX power table */ + switch (ah->ah_radio) { + case AR5K_RF5111: + type = AR5K_PWRTABLE_PWR_TO_PCDAC; + break; + case AR5K_RF5112: + type = AR5K_PWRTABLE_LINEAR_PCDAC; + break; + case AR5K_RF2413: + case AR5K_RF5413: + case AR5K_RF2316: + case AR5K_RF2317: + case AR5K_RF2425: + type = AR5K_PWRTABLE_PWR_TO_PDADC; + break; + default: + return -EINVAL; + } + + /* FIXME: Only on channel/mode change */ + ret = ath5k_setup_channel_powertable(ah, channel, ee_mode, type); + if (ret) + return ret; + + /* Limit max power if we have a CTL available */ + ath5k_get_max_ctl_power(ah, channel); + + /* FIXME: Tx power limit for this regdomain + * XXX: Mac80211/CRDA will do that anyway ? */ + + /* FIXME: Antenna reduction stuff */ + + /* FIXME: Limit power on turbo modes */ + + /* FIXME: TPC scale reduction */ + + /* Get surounding channels for per-rate power table + * calibration */ + ath5k_get_rate_pcal_data(ah, channel, &rate_info); + + /* Setup rate power table */ + ath5k_setup_rate_powertable(ah, txpower, &rate_info, ee_mode); + + /* Write rate power table on hw */ + ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) | + AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) | + AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1); + + ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(7, 24) | + AR5K_TXPOWER_OFDM(6, 16) | AR5K_TXPOWER_OFDM(5, 8) | + AR5K_TXPOWER_OFDM(4, 0), AR5K_PHY_TXPOWER_RATE2); + + ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(10, 24) | + AR5K_TXPOWER_CCK(9, 16) | AR5K_TXPOWER_CCK(15, 8) | + AR5K_TXPOWER_CCK(8, 0), AR5K_PHY_TXPOWER_RATE3); + + ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(14, 24) | + AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) | + AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4); + + /* FIXME: TPC support */ + if (ah->ah_txpower.txp_tpc) { + ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE | + AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); + + ath5k_hw_reg_write(ah, + AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) | + AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) | + AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP), + AR5K_TPC); + } else { + ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX | + AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); + } + + return 0; +} + +int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower) +{ + /*Just a try M.F.*/ + struct ieee80211_channel *channel = &ah->ah_current_channel; + u8 ee_mode; + + ATH5K_TRACE(ah->ah_sc); + + switch (channel->hw_value & CHANNEL_MODES) { + case CHANNEL_A: + case CHANNEL_T: + case CHANNEL_XR: + ee_mode = AR5K_EEPROM_MODE_11A; + break; + case CHANNEL_G: + case CHANNEL_TG: + ee_mode = AR5K_EEPROM_MODE_11G; + break; + case CHANNEL_B: + ee_mode = AR5K_EEPROM_MODE_11B; + break; + default: + ATH5K_ERR(ah->ah_sc, + "invalid channel: %d\n", channel->center_freq); + return -EINVAL; + } + + ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_TXPOWER, + "changing txpower to %d\n", txpower); + + return ath5k_hw_txpower(ah, channel, ee_mode, txpower); +} + +#undef _ATH5K_PHY diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c new file mode 100644 index 00000000000..73407b3f53e --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -0,0 +1,549 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +/********************************************\ +Queue Control Unit, DFS Control Unit Functions +\********************************************/ + +#include "ath5k.h" +#include "reg.h" +#include "debug.h" +#include "base.h" + +/* + * Get properties for a transmit queue + */ +int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, + struct ath5k_txq_info *queue_info) +{ + ATH5K_TRACE(ah->ah_sc); + memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info)); + return 0; +} + +/* + * Set properties for a transmit queue + */ +int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, + const struct ath5k_txq_info *queue_info) +{ + ATH5K_TRACE(ah->ah_sc); + AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + + if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) + return -EIO; + + memcpy(&ah->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info)); + + /*XXX: Is this supported on 5210 ?*/ + if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA && + ((queue_info->tqi_subtype == AR5K_WME_AC_VI) || + (queue_info->tqi_subtype == AR5K_WME_AC_VO))) || + queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD) + ah->ah_txq[queue].tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS; + + return 0; +} + +/* + * Initialize a transmit queue + */ +int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, + struct ath5k_txq_info *queue_info) +{ + unsigned int queue; + int ret; + + ATH5K_TRACE(ah->ah_sc); + + /* + * Get queue by type + */ + /*5210 only has 2 queues*/ + if (ah->ah_version == AR5K_AR5210) { + switch (queue_type) { + case AR5K_TX_QUEUE_DATA: + queue = AR5K_TX_QUEUE_ID_NOQCU_DATA; + break; + case AR5K_TX_QUEUE_BEACON: + case AR5K_TX_QUEUE_CAB: + queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON; + break; + default: + return -EINVAL; + } + } else { + switch (queue_type) { + case AR5K_TX_QUEUE_DATA: + for (queue = AR5K_TX_QUEUE_ID_DATA_MIN; + ah->ah_txq[queue].tqi_type != + AR5K_TX_QUEUE_INACTIVE; queue++) { + + if (queue > AR5K_TX_QUEUE_ID_DATA_MAX) + return -EINVAL; + } + break; + case AR5K_TX_QUEUE_UAPSD: + queue = AR5K_TX_QUEUE_ID_UAPSD; + break; + case AR5K_TX_QUEUE_BEACON: + queue = AR5K_TX_QUEUE_ID_BEACON; + break; + case AR5K_TX_QUEUE_CAB: + queue = AR5K_TX_QUEUE_ID_CAB; + break; + case AR5K_TX_QUEUE_XR_DATA: + if (ah->ah_version != AR5K_AR5212) + ATH5K_ERR(ah->ah_sc, + "XR data queues only supported in" + " 5212!\n"); + queue = AR5K_TX_QUEUE_ID_XR_DATA; + break; + default: + return -EINVAL; + } + } + + /* + * Setup internal queue structure + */ + memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info)); + ah->ah_txq[queue].tqi_type = queue_type; + + if (queue_info != NULL) { + queue_info->tqi_type = queue_type; + ret = ath5k_hw_set_tx_queueprops(ah, queue, queue_info); + if (ret) + return ret; + } + + /* + * We use ah_txq_status to hold a temp value for + * the Secondary interrupt mask registers on 5211+ + * check out ath5k_hw_reset_tx_queue + */ + AR5K_Q_ENABLE_BITS(ah->ah_txq_status, queue); + + return queue; +} + +/* + * Get number of pending frames + * for a specific queue [5211+] + */ +u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) +{ + u32 pending; + ATH5K_TRACE(ah->ah_sc); + AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + + /* Return if queue is declared inactive */ + if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) + return false; + + /* XXX: How about AR5K_CFG_TXCNT ? */ + if (ah->ah_version == AR5K_AR5210) + return false; + + pending = ath5k_hw_reg_read(ah, AR5K_QUEUE_STATUS(queue)); + pending &= AR5K_QCU_STS_FRMPENDCNT; + + /* It's possible to have no frames pending even if TXE + * is set. To indicate that q has not stopped return + * true */ + if (!pending && AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) + return true; + + return pending; +} + +/* + * Set a transmit queue inactive + */ +void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) +{ + ATH5K_TRACE(ah->ah_sc); + if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num)) + return; + + /* This queue will be skipped in further operations */ + ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE; + /*For SIMR setup*/ + AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue); +} + +/* + * Set DFS properties for a transmit queue on DCU + */ +int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) +{ + u32 cw_min, cw_max, retry_lg, retry_sh; + struct ath5k_txq_info *tq = &ah->ah_txq[queue]; + + ATH5K_TRACE(ah->ah_sc); + AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + + tq = &ah->ah_txq[queue]; + + if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE) + return 0; + + if (ah->ah_version == AR5K_AR5210) { + /* Only handle data queues, others will be ignored */ + if (tq->tqi_type != AR5K_TX_QUEUE_DATA) + return 0; + + /* Set Slot time */ + ath5k_hw_reg_write(ah, ah->ah_turbo ? + AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME, + AR5K_SLOT_TIME); + /* Set ACK_CTS timeout */ + ath5k_hw_reg_write(ah, ah->ah_turbo ? + AR5K_INIT_ACK_CTS_TIMEOUT_TURBO : + AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME); + /* Set Transmit Latency */ + ath5k_hw_reg_write(ah, ah->ah_turbo ? + AR5K_INIT_TRANSMIT_LATENCY_TURBO : + AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210); + + /* Set IFS0 */ + if (ah->ah_turbo) { + ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO + + (ah->ah_aifs + tq->tqi_aifs) * + AR5K_INIT_SLOT_TIME_TURBO) << + AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO, + AR5K_IFS0); + } else { + ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS + + (ah->ah_aifs + tq->tqi_aifs) * + AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) | + AR5K_INIT_SIFS, AR5K_IFS0); + } + + /* Set IFS1 */ + ath5k_hw_reg_write(ah, ah->ah_turbo ? + AR5K_INIT_PROTO_TIME_CNTRL_TURBO : + AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1); + /* Set AR5K_PHY_SETTLING */ + ath5k_hw_reg_write(ah, ah->ah_turbo ? + (ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F) + | 0x38 : + (ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F) + | 0x1C, + AR5K_PHY_SETTLING); + /* Set Frame Control Register */ + ath5k_hw_reg_write(ah, ah->ah_turbo ? + (AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE | + AR5K_PHY_TURBO_SHORT | 0x2020) : + (AR5K_PHY_FRAME_CTL_INI | 0x1020), + AR5K_PHY_FRAME_CTL_5210); + } + + /* + * Calculate cwmin/max by channel mode + */ + cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN; + cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX; + ah->ah_aifs = AR5K_TUNE_AIFS; + /*XR is only supported on 5212*/ + if (IS_CHAN_XR(ah->ah_current_channel) && + ah->ah_version == AR5K_AR5212) { + cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_XR; + cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_XR; + ah->ah_aifs = AR5K_TUNE_AIFS_XR; + /*B mode is not supported on 5210*/ + } else if (IS_CHAN_B(ah->ah_current_channel) && + ah->ah_version != AR5K_AR5210) { + cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_11B; + cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_11B; + ah->ah_aifs = AR5K_TUNE_AIFS_11B; + } + + cw_min = 1; + while (cw_min < ah->ah_cw_min) + cw_min = (cw_min << 1) | 1; + + cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) : + ((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1); + cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) : + ((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1); + + /* + * Calculate and set retry limits + */ + if (ah->ah_software_retry) { + /* XXX Need to test this */ + retry_lg = ah->ah_limit_tx_retries; + retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ? + AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg; + } else { + retry_lg = AR5K_INIT_LG_RETRY; + retry_sh = AR5K_INIT_SH_RETRY; + } + + /*No QCU/DCU [5210]*/ + if (ah->ah_version == AR5K_AR5210) { + ath5k_hw_reg_write(ah, + (cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S) + | AR5K_REG_SM(AR5K_INIT_SLG_RETRY, + AR5K_NODCU_RETRY_LMT_SLG_RETRY) + | AR5K_REG_SM(AR5K_INIT_SSH_RETRY, + AR5K_NODCU_RETRY_LMT_SSH_RETRY) + | AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY) + | AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY), + AR5K_NODCU_RETRY_LMT); + } else { + /*QCU/DCU [5211+]*/ + ath5k_hw_reg_write(ah, + AR5K_REG_SM(AR5K_INIT_SLG_RETRY, + AR5K_DCU_RETRY_LMT_SLG_RETRY) | + AR5K_REG_SM(AR5K_INIT_SSH_RETRY, + AR5K_DCU_RETRY_LMT_SSH_RETRY) | + AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) | + AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY), + AR5K_QUEUE_DFS_RETRY_LIMIT(queue)); + + /*===Rest is also for QCU/DCU only [5211+]===*/ + + /* + * Set initial content window (cw_min/cw_max) + * and arbitrated interframe space (aifs)... + */ + ath5k_hw_reg_write(ah, + AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) | + AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) | + AR5K_REG_SM(ah->ah_aifs + tq->tqi_aifs, + AR5K_DCU_LCL_IFS_AIFS), + AR5K_QUEUE_DFS_LOCAL_IFS(queue)); + + /* + * Set misc registers + */ + /* Enable DCU early termination for this queue */ + AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_DCU_EARLY); + + /* Enable DCU to wait for next fragment from QCU */ + AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), + AR5K_DCU_MISC_FRAG_WAIT); + + /* On Maui and Spirit use the global seqnum on DCU */ + if (ah->ah_mac_version < AR5K_SREV_AR5211) + AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), + AR5K_DCU_MISC_SEQNUM_CTL); + + if (tq->tqi_cbr_period) { + ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period, + AR5K_QCU_CBRCFG_INTVAL) | + AR5K_REG_SM(tq->tqi_cbr_overflow_limit, + AR5K_QCU_CBRCFG_ORN_THRES), + AR5K_QUEUE_CBRCFG(queue)); + AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_FRSHED_CBR); + if (tq->tqi_cbr_overflow_limit) + AR5K_REG_ENABLE_BITS(ah, + AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_CBR_THRES_ENABLE); + } + + if (tq->tqi_ready_time && + (tq->tqi_type != AR5K_TX_QUEUE_ID_CAB)) + ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time, + AR5K_QCU_RDYTIMECFG_INTVAL) | + AR5K_QCU_RDYTIMECFG_ENABLE, + AR5K_QUEUE_RDYTIMECFG(queue)); + + if (tq->tqi_burst_time) { + ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time, + AR5K_DCU_CHAN_TIME_DUR) | + AR5K_DCU_CHAN_TIME_ENABLE, + AR5K_QUEUE_DFS_CHANNEL_TIME(queue)); + + if (tq->tqi_flags + & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) + AR5K_REG_ENABLE_BITS(ah, + AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_RDY_VEOL_POLICY); + } + + if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE) + ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS, + AR5K_QUEUE_DFS_MISC(queue)); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) + ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG, + AR5K_QUEUE_DFS_MISC(queue)); + + /* + * Set registers by queue type + */ + switch (tq->tqi_type) { + case AR5K_TX_QUEUE_BEACON: + AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_FRSHED_DBA_GT | + AR5K_QCU_MISC_CBREXP_BCN_DIS | + AR5K_QCU_MISC_BCN_ENABLE); + + AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), + (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << + AR5K_DCU_MISC_ARBLOCK_CTL_S) | + AR5K_DCU_MISC_ARBLOCK_IGNORE | + AR5K_DCU_MISC_POST_FR_BKOFF_DIS | + AR5K_DCU_MISC_BCN_ENABLE); + break; + + case AR5K_TX_QUEUE_CAB: + AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_FRSHED_BCN_SENT_GT | + AR5K_QCU_MISC_CBREXP_DIS | + AR5K_QCU_MISC_RDY_VEOL_POLICY | + AR5K_QCU_MISC_CBREXP_BCN_DIS); + + ath5k_hw_reg_write(ah, ((AR5K_TUNE_BEACON_INTERVAL - + (AR5K_TUNE_SW_BEACON_RESP - + AR5K_TUNE_DMA_BEACON_RESP) - + AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) | + AR5K_QCU_RDYTIMECFG_ENABLE, + AR5K_QUEUE_RDYTIMECFG(queue)); + + AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), + (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << + AR5K_DCU_MISC_ARBLOCK_CTL_S)); + break; + + case AR5K_TX_QUEUE_UAPSD: + AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_CBREXP_DIS); + break; + + case AR5K_TX_QUEUE_DATA: + default: + break; + } + + /* TODO: Handle frame compression */ + + /* + * Enable interrupts for this tx queue + * in the secondary interrupt mask registers + */ + if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE) + AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue); + + /* Update secondary interrupt mask registers */ + + /* Filter out inactive queues */ + ah->ah_txq_imr_txok &= ah->ah_txq_status; + ah->ah_txq_imr_txerr &= ah->ah_txq_status; + ah->ah_txq_imr_txurn &= ah->ah_txq_status; + ah->ah_txq_imr_txdesc &= ah->ah_txq_status; + ah->ah_txq_imr_txeol &= ah->ah_txq_status; + ah->ah_txq_imr_cbrorn &= ah->ah_txq_status; + ah->ah_txq_imr_cbrurn &= ah->ah_txq_status; + ah->ah_txq_imr_qtrig &= ah->ah_txq_status; + ah->ah_txq_imr_nofrm &= ah->ah_txq_status; + + ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok, + AR5K_SIMR0_QCU_TXOK) | + AR5K_REG_SM(ah->ah_txq_imr_txdesc, + AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0); + ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr, + AR5K_SIMR1_QCU_TXERR) | + AR5K_REG_SM(ah->ah_txq_imr_txeol, + AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1); + /* Update simr2 but don't overwrite rest simr2 settings */ + AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN); + AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2, + AR5K_REG_SM(ah->ah_txq_imr_txurn, + AR5K_SIMR2_QCU_TXURN)); + ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn, + AR5K_SIMR3_QCBRORN) | + AR5K_REG_SM(ah->ah_txq_imr_cbrurn, + AR5K_SIMR3_QCBRURN), AR5K_SIMR3); + ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig, + AR5K_SIMR4_QTRIG), AR5K_SIMR4); + /* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */ + ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm, + AR5K_TXNOFRM_QCU), AR5K_TXNOFRM); + /* No queue has TXNOFRM enabled, disable the interrupt + * by setting AR5K_TXNOFRM to zero */ + if (ah->ah_txq_imr_nofrm == 0) + ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM); + + /* Set QCU mask for this DCU to save power */ + AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(queue), queue); + } + + return 0; +} + +/* + * Get slot time from DCU + */ +unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah) +{ + ATH5K_TRACE(ah->ah_sc); + if (ah->ah_version == AR5K_AR5210) + return ath5k_hw_clocktoh(ath5k_hw_reg_read(ah, + AR5K_SLOT_TIME) & 0xffff, ah->ah_turbo); + else + return ath5k_hw_reg_read(ah, AR5K_DCU_GBL_IFS_SLOT) & 0xffff; +} + +/* + * Set slot time on DCU + */ +int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time) +{ + ATH5K_TRACE(ah->ah_sc); + if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX) + return -EINVAL; + + if (ah->ah_version == AR5K_AR5210) + ath5k_hw_reg_write(ah, ath5k_hw_htoclock(slot_time, + ah->ah_turbo), AR5K_SLOT_TIME); + else + ath5k_hw_reg_write(ah, slot_time, AR5K_DCU_GBL_IFS_SLOT); + + return 0; +} + diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h new file mode 100644 index 00000000000..6809b54a2ad --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/reg.h @@ -0,0 +1,2596 @@ +/* + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2007-2008 Michael Taylor <mike.taylor@apprion.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +/* + * Register values for Atheros 5210/5211/5212 cards from OpenBSD's ar5k + * maintained by Reyk Floeter + * + * I tried to document those registers by looking at ar5k code, some + * 802.11 (802.11e mostly) papers and by reading various public available + * Atheros presentations and papers like these: + * + * 5210 - http://nova.stanford.edu/~bbaas/ps/isscc2002_slides.pdf + * http://www.it.iitb.ac.in/~janak/wifire/01222734.pdf + * + * 5211 - http://www.hotchips.org/archives/hc14/3_Tue/16_mcfarland.pdf + * + * This file also contains register values found on a memory dump of + * Atheros's ART program (Atheros Radio Test), on ath9k, on legacy-hal + * released by Atheros and on various debug messages found on the net. + */ + + + +/*====MAC DMA REGISTERS====*/ + +/* + * AR5210-Specific TXDP registers + * 5210 has only 2 transmit queues so no DCU/QCU, just + * 2 transmit descriptor pointers... + */ +#define AR5K_NOQCU_TXDP0 0x0000 /* Queue 0 - data */ +#define AR5K_NOQCU_TXDP1 0x0004 /* Queue 1 - beacons */ + +/* + * Mac Control Register + */ +#define AR5K_CR 0x0008 /* Register Address */ +#define AR5K_CR_TXE0 0x00000001 /* TX Enable for queue 0 on 5210 */ +#define AR5K_CR_TXE1 0x00000002 /* TX Enable for queue 1 on 5210 */ +#define AR5K_CR_RXE 0x00000004 /* RX Enable */ +#define AR5K_CR_TXD0 0x00000008 /* TX Disable for queue 0 on 5210 */ +#define AR5K_CR_TXD1 0x00000010 /* TX Disable for queue 1 on 5210 */ +#define AR5K_CR_RXD 0x00000020 /* RX Disable */ +#define AR5K_CR_SWI 0x00000040 /* Software Interrupt */ + +/* + * RX Descriptor Pointer register + */ +#define AR5K_RXDP 0x000c + +/* + * Configuration and status register + */ +#define AR5K_CFG 0x0014 /* Register Address */ +#define AR5K_CFG_SWTD 0x00000001 /* Byte-swap TX descriptor (for big endian archs) */ +#define AR5K_CFG_SWTB 0x00000002 /* Byte-swap TX buffer */ +#define AR5K_CFG_SWRD 0x00000004 /* Byte-swap RX descriptor */ +#define AR5K_CFG_SWRB 0x00000008 /* Byte-swap RX buffer */ +#define AR5K_CFG_SWRG 0x00000010 /* Byte-swap Register access */ +#define AR5K_CFG_IBSS 0x00000020 /* 0-BSS, 1-IBSS [5211+] */ +#define AR5K_CFG_PHY_OK 0x00000100 /* [5211+] */ +#define AR5K_CFG_EEBS 0x00000200 /* EEPROM is busy */ +#define AR5K_CFG_CLKGD 0x00000400 /* Clock gated (Disable dynamic clock) */ +#define AR5K_CFG_TXCNT 0x00007800 /* Tx frame count (?) [5210] */ +#define AR5K_CFG_TXCNT_S 11 +#define AR5K_CFG_TXFSTAT 0x00008000 /* Tx frame status (?) [5210] */ +#define AR5K_CFG_TXFSTRT 0x00010000 /* [5210] */ +#define AR5K_CFG_PCI_THRES 0x00060000 /* PCI Master req q threshold [5211+] */ +#define AR5K_CFG_PCI_THRES_S 17 + +/* + * Interrupt enable register + */ +#define AR5K_IER 0x0024 /* Register Address */ +#define AR5K_IER_DISABLE 0x00000000 /* Disable card interrupts */ +#define AR5K_IER_ENABLE 0x00000001 /* Enable card interrupts */ + + +/* + * 0x0028 is Beacon Control Register on 5210 + * and first RTS duration register on 5211 + */ + +/* + * Beacon control register [5210] + */ +#define AR5K_BCR 0x0028 /* Register Address */ +#define AR5K_BCR_AP 0x00000000 /* AP mode */ +#define AR5K_BCR_ADHOC 0x00000001 /* Ad-Hoc mode */ +#define AR5K_BCR_BDMAE 0x00000002 /* DMA enable */ +#define AR5K_BCR_TQ1FV 0x00000004 /* Use Queue1 for CAB traffic */ +#define AR5K_BCR_TQ1V 0x00000008 /* Use Queue1 for Beacon traffic */ +#define AR5K_BCR_BCGET 0x00000010 + +/* + * First RTS duration register [5211] + */ +#define AR5K_RTSD0 0x0028 /* Register Address */ +#define AR5K_RTSD0_6 0x000000ff /* 6Mb RTS duration mask (?) */ +#define AR5K_RTSD0_6_S 0 /* 6Mb RTS duration shift (?) */ +#define AR5K_RTSD0_9 0x0000ff00 /* 9Mb*/ +#define AR5K_RTSD0_9_S 8 +#define AR5K_RTSD0_12 0x00ff0000 /* 12Mb*/ +#define AR5K_RTSD0_12_S 16 +#define AR5K_RTSD0_18 0xff000000 /* 16Mb*/ +#define AR5K_RTSD0_18_S 24 + + +/* + * 0x002c is Beacon Status Register on 5210 + * and second RTS duration register on 5211 + */ + +/* + * Beacon status register [5210] + * + * As i can see in ar5k_ar5210_tx_start Reyk uses some of the values of BCR + * for this register, so i guess TQ1V,TQ1FV and BDMAE have the same meaning + * here and SNP/SNAP means "snapshot" (so this register gets synced with BCR). + * So SNAPPEDBCRVALID sould also stand for "snapped BCR -values- valid", so i + * renamed it to SNAPSHOTSVALID to make more sense. I realy have no idea what + * else can it be. I also renamed SNPBCMD to SNPADHOC to match BCR. + */ +#define AR5K_BSR 0x002c /* Register Address */ +#define AR5K_BSR_BDLYSW 0x00000001 /* SW Beacon delay (?) */ +#define AR5K_BSR_BDLYDMA 0x00000002 /* DMA Beacon delay (?) */ +#define AR5K_BSR_TXQ1F 0x00000004 /* Beacon queue (1) finished */ +#define AR5K_BSR_ATIMDLY 0x00000008 /* ATIM delay (?) */ +#define AR5K_BSR_SNPADHOC 0x00000100 /* Ad-hoc mode set (?) */ +#define AR5K_BSR_SNPBDMAE 0x00000200 /* Beacon DMA enabled (?) */ +#define AR5K_BSR_SNPTQ1FV 0x00000400 /* Queue1 is used for CAB traffic (?) */ +#define AR5K_BSR_SNPTQ1V 0x00000800 /* Queue1 is used for Beacon traffic (?) */ +#define AR5K_BSR_SNAPSHOTSVALID 0x00001000 /* BCR snapshots are valid (?) */ +#define AR5K_BSR_SWBA_CNT 0x00ff0000 + +/* + * Second RTS duration register [5211] + */ +#define AR5K_RTSD1 0x002c /* Register Address */ +#define AR5K_RTSD1_24 0x000000ff /* 24Mb */ +#define AR5K_RTSD1_24_S 0 +#define AR5K_RTSD1_36 0x0000ff00 /* 36Mb */ +#define AR5K_RTSD1_36_S 8 +#define AR5K_RTSD1_48 0x00ff0000 /* 48Mb */ +#define AR5K_RTSD1_48_S 16 +#define AR5K_RTSD1_54 0xff000000 /* 54Mb */ +#define AR5K_RTSD1_54_S 24 + + +/* + * Transmit configuration register + */ +#define AR5K_TXCFG 0x0030 /* Register Address */ +#define AR5K_TXCFG_SDMAMR 0x00000007 /* DMA size (read) */ +#define AR5K_TXCFG_SDMAMR_S 0 +#define AR5K_TXCFG_B_MODE 0x00000008 /* Set b mode for 5111 (enable 2111) */ +#define AR5K_TXCFG_TXFSTP 0x00000008 /* TX DMA full Stop [5210] */ +#define AR5K_TXCFG_TXFULL 0x000003f0 /* TX Triger level mask */ +#define AR5K_TXCFG_TXFULL_S 4 +#define AR5K_TXCFG_TXFULL_0B 0x00000000 +#define AR5K_TXCFG_TXFULL_64B 0x00000010 +#define AR5K_TXCFG_TXFULL_128B 0x00000020 +#define AR5K_TXCFG_TXFULL_192B 0x00000030 +#define AR5K_TXCFG_TXFULL_256B 0x00000040 +#define AR5K_TXCFG_TXCONT_EN 0x00000080 +#define AR5K_TXCFG_DMASIZE 0x00000100 /* Flag for passing DMA size [5210] */ +#define AR5K_TXCFG_JUMBO_DESC_EN 0x00000400 /* Enable jumbo tx descriptors [5211+] */ +#define AR5K_TXCFG_ADHOC_BCN_ATIM 0x00000800 /* Adhoc Beacon ATIM Policy */ +#define AR5K_TXCFG_ATIM_WINDOW_DEF_DIS 0x00001000 /* Disable ATIM window defer [5211+] */ +#define AR5K_TXCFG_RTSRND 0x00001000 /* [5211+] */ +#define AR5K_TXCFG_FRMPAD_DIS 0x00002000 /* [5211+] */ +#define AR5K_TXCFG_RDY_CBR_DIS 0x00004000 /* Ready time CBR disable [5211+] */ +#define AR5K_TXCFG_JUMBO_FRM_MODE 0x00008000 /* Jumbo frame mode [5211+] */ +#define AR5K_TXCFG_DCU_DBL_BUF_DIS 0x00008000 /* Disable double buffering on DCU */ +#define AR5K_TXCFG_DCU_CACHING_DIS 0x00010000 /* Disable DCU caching */ + +/* + * Receive configuration register + */ +#define AR5K_RXCFG 0x0034 /* Register Address */ +#define AR5K_RXCFG_SDMAMW 0x00000007 /* DMA size (write) */ +#define AR5K_RXCFG_SDMAMW_S 0 +#define AR5K_RXCFG_ZLFDMA 0x00000008 /* Enable Zero-length frame DMA */ +#define AR5K_RXCFG_DEF_ANTENNA 0x00000010 /* Default antenna (?) */ +#define AR5K_RXCFG_JUMBO_RXE 0x00000020 /* Enable jumbo rx descriptors [5211+] */ +#define AR5K_RXCFG_JUMBO_WRAP 0x00000040 /* Wrap jumbo frames [5211+] */ +#define AR5K_RXCFG_SLE_ENTRY 0x00000080 /* Sleep entry policy */ + +/* + * Receive jumbo descriptor last address register + * Only found in 5211 (?) + */ +#define AR5K_RXJLA 0x0038 + +/* + * MIB control register + */ +#define AR5K_MIBC 0x0040 /* Register Address */ +#define AR5K_MIBC_COW 0x00000001 /* Warn test indicator */ +#define AR5K_MIBC_FMC 0x00000002 /* Freeze MIB Counters */ +#define AR5K_MIBC_CMC 0x00000004 /* Clean MIB Counters */ +#define AR5K_MIBC_MCS 0x00000008 /* MIB counter strobe */ + +/* + * Timeout prescale register + */ +#define AR5K_TOPS 0x0044 +#define AR5K_TOPS_M 0x0000ffff + +/* + * Receive timeout register (no frame received) + */ +#define AR5K_RXNOFRM 0x0048 +#define AR5K_RXNOFRM_M 0x000003ff + +/* + * Transmit timeout register (no frame sent) + */ +#define AR5K_TXNOFRM 0x004c +#define AR5K_TXNOFRM_M 0x000003ff +#define AR5K_TXNOFRM_QCU 0x000ffc00 +#define AR5K_TXNOFRM_QCU_S 10 + +/* + * Receive frame gap timeout register + */ +#define AR5K_RPGTO 0x0050 +#define AR5K_RPGTO_M 0x000003ff + +/* + * Receive frame count limit register + */ +#define AR5K_RFCNT 0x0054 +#define AR5K_RFCNT_M 0x0000001f /* [5211+] (?) */ +#define AR5K_RFCNT_RFCL 0x0000000f /* [5210] */ + +/* + * Misc settings register + * (reserved0-3) + */ +#define AR5K_MISC 0x0058 /* Register Address */ +#define AR5K_MISC_DMA_OBS_M 0x000001e0 +#define AR5K_MISC_DMA_OBS_S 5 +#define AR5K_MISC_MISC_OBS_M 0x00000e00 +#define AR5K_MISC_MISC_OBS_S 9 +#define AR5K_MISC_MAC_OBS_LSB_M 0x00007000 +#define AR5K_MISC_MAC_OBS_LSB_S 12 +#define AR5K_MISC_MAC_OBS_MSB_M 0x00038000 +#define AR5K_MISC_MAC_OBS_MSB_S 15 +#define AR5K_MISC_LED_DECAY 0x001c0000 /* [5210] */ +#define AR5K_MISC_LED_BLINK 0x00e00000 /* [5210] */ + +/* + * QCU/DCU clock gating register (5311) + * (reserved4-5) + */ +#define AR5K_QCUDCU_CLKGT 0x005c /* Register Address (?) */ +#define AR5K_QCUDCU_CLKGT_QCU 0x0000ffff /* Mask for QCU clock */ +#define AR5K_QCUDCU_CLKGT_DCU 0x07ff0000 /* Mask for DCU clock */ + +/* + * Interrupt Status Registers + * + * For 5210 there is only one status register but for + * 5211/5212 we have one primary and 4 secondary registers. + * So we have AR5K_ISR for 5210 and AR5K_PISR /SISRx for 5211/5212. + * Most of these bits are common for all chipsets. + */ +#define AR5K_ISR 0x001c /* Register Address [5210] */ +#define AR5K_PISR 0x0080 /* Register Address [5211+] */ +#define AR5K_ISR_RXOK 0x00000001 /* Frame successfuly recieved */ +#define AR5K_ISR_RXDESC 0x00000002 /* RX descriptor request */ +#define AR5K_ISR_RXERR 0x00000004 /* Receive error */ +#define AR5K_ISR_RXNOFRM 0x00000008 /* No frame received (receive timeout) */ +#define AR5K_ISR_RXEOL 0x00000010 /* Empty RX descriptor */ +#define AR5K_ISR_RXORN 0x00000020 /* Receive FIFO overrun */ +#define AR5K_ISR_TXOK 0x00000040 /* Frame successfuly transmited */ +#define AR5K_ISR_TXDESC 0x00000080 /* TX descriptor request */ +#define AR5K_ISR_TXERR 0x00000100 /* Transmit error */ +#define AR5K_ISR_TXNOFRM 0x00000200 /* No frame transmited (transmit timeout) */ +#define AR5K_ISR_TXEOL 0x00000400 /* Empty TX descriptor */ +#define AR5K_ISR_TXURN 0x00000800 /* Transmit FIFO underrun */ +#define AR5K_ISR_MIB 0x00001000 /* Update MIB counters */ +#define AR5K_ISR_SWI 0x00002000 /* Software interrupt */ +#define AR5K_ISR_RXPHY 0x00004000 /* PHY error */ +#define AR5K_ISR_RXKCM 0x00008000 /* RX Key cache miss */ +#define AR5K_ISR_SWBA 0x00010000 /* Software beacon alert */ +#define AR5K_ISR_BRSSI 0x00020000 /* Beacon rssi below threshold (?) */ +#define AR5K_ISR_BMISS 0x00040000 /* Beacon missed */ +#define AR5K_ISR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */ +#define AR5K_ISR_BNR 0x00100000 /* Beacon not ready [5211+] */ +#define AR5K_ISR_MCABT 0x00100000 /* Master Cycle Abort [5210] */ +#define AR5K_ISR_RXCHIRP 0x00200000 /* CHIRP Received [5212+] */ +#define AR5K_ISR_SSERR 0x00200000 /* Signaled System Error [5210] */ +#define AR5K_ISR_DPERR 0x00400000 /* Det par Error (?) [5210] */ +#define AR5K_ISR_RXDOPPLER 0x00400000 /* Doppler chirp received [5212+] */ +#define AR5K_ISR_TIM 0x00800000 /* [5211+] */ +#define AR5K_ISR_BCNMISC 0x00800000 /* 'or' of TIM, CAB_END, DTIM_SYNC, BCN_TIMEOUT, + CAB_TIMEOUT and DTIM bits from SISR2 [5212+] */ +#define AR5K_ISR_GPIO 0x01000000 /* GPIO (rf kill) */ +#define AR5K_ISR_QCBRORN 0x02000000 /* QCU CBR overrun [5211+] */ +#define AR5K_ISR_QCBRURN 0x04000000 /* QCU CBR underrun [5211+] */ +#define AR5K_ISR_QTRIG 0x08000000 /* QCU scheduling trigger [5211+] */ + +/* + * Secondary status registers [5211+] (0 - 4) + * + * These give the status for each QCU, only QCUs 0-9 are + * represented. + */ +#define AR5K_SISR0 0x0084 /* Register Address [5211+] */ +#define AR5K_SISR0_QCU_TXOK 0x000003ff /* Mask for QCU_TXOK */ +#define AR5K_SISR0_QCU_TXOK_S 0 +#define AR5K_SISR0_QCU_TXDESC 0x03ff0000 /* Mask for QCU_TXDESC */ +#define AR5K_SISR0_QCU_TXDESC_S 16 + +#define AR5K_SISR1 0x0088 /* Register Address [5211+] */ +#define AR5K_SISR1_QCU_TXERR 0x000003ff /* Mask for QCU_TXERR */ +#define AR5K_SISR1_QCU_TXERR_S 0 +#define AR5K_SISR1_QCU_TXEOL 0x03ff0000 /* Mask for QCU_TXEOL */ +#define AR5K_SISR1_QCU_TXEOL_S 16 + +#define AR5K_SISR2 0x008c /* Register Address [5211+] */ +#define AR5K_SISR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */ +#define AR5K_SISR2_QCU_TXURN_S 0 +#define AR5K_SISR2_MCABT 0x00100000 /* Master Cycle Abort */ +#define AR5K_SISR2_SSERR 0x00200000 /* Signaled System Error */ +#define AR5K_SISR2_DPERR 0x00400000 /* Bus parity error */ +#define AR5K_SISR2_TIM 0x01000000 /* [5212+] */ +#define AR5K_SISR2_CAB_END 0x02000000 /* [5212+] */ +#define AR5K_SISR2_DTIM_SYNC 0x04000000 /* DTIM sync lost [5212+] */ +#define AR5K_SISR2_BCN_TIMEOUT 0x08000000 /* Beacon Timeout [5212+] */ +#define AR5K_SISR2_CAB_TIMEOUT 0x10000000 /* CAB Timeout [5212+] */ +#define AR5K_SISR2_DTIM 0x20000000 /* [5212+] */ +#define AR5K_SISR2_TSFOOR 0x80000000 /* TSF OOR (?) */ + +#define AR5K_SISR3 0x0090 /* Register Address [5211+] */ +#define AR5K_SISR3_QCBRORN 0x000003ff /* Mask for QCBRORN */ +#define AR5K_SISR3_QCBRORN_S 0 +#define AR5K_SISR3_QCBRURN 0x03ff0000 /* Mask for QCBRURN */ +#define AR5K_SISR3_QCBRURN_S 16 + +#define AR5K_SISR4 0x0094 /* Register Address [5211+] */ +#define AR5K_SISR4_QTRIG 0x000003ff /* Mask for QTRIG */ +#define AR5K_SISR4_QTRIG_S 0 + +/* + * Shadow read-and-clear interrupt status registers [5211+] + */ +#define AR5K_RAC_PISR 0x00c0 /* Read and clear PISR */ +#define AR5K_RAC_SISR0 0x00c4 /* Read and clear SISR0 */ +#define AR5K_RAC_SISR1 0x00c8 /* Read and clear SISR1 */ +#define AR5K_RAC_SISR2 0x00cc /* Read and clear SISR2 */ +#define AR5K_RAC_SISR3 0x00d0 /* Read and clear SISR3 */ +#define AR5K_RAC_SISR4 0x00d4 /* Read and clear SISR4 */ + +/* + * Interrupt Mask Registers + * + * As whith ISRs 5210 has one IMR (AR5K_IMR) and 5211/5212 has one primary + * (AR5K_PIMR) and 4 secondary IMRs (AR5K_SIMRx). Note that ISR/IMR flags match. + */ +#define AR5K_IMR 0x0020 /* Register Address [5210] */ +#define AR5K_PIMR 0x00a0 /* Register Address [5211+] */ +#define AR5K_IMR_RXOK 0x00000001 /* Frame successfuly recieved*/ +#define AR5K_IMR_RXDESC 0x00000002 /* RX descriptor request*/ +#define AR5K_IMR_RXERR 0x00000004 /* Receive error*/ +#define AR5K_IMR_RXNOFRM 0x00000008 /* No frame received (receive timeout)*/ +#define AR5K_IMR_RXEOL 0x00000010 /* Empty RX descriptor*/ +#define AR5K_IMR_RXORN 0x00000020 /* Receive FIFO overrun*/ +#define AR5K_IMR_TXOK 0x00000040 /* Frame successfuly transmited*/ +#define AR5K_IMR_TXDESC 0x00000080 /* TX descriptor request*/ +#define AR5K_IMR_TXERR 0x00000100 /* Transmit error*/ +#define AR5K_IMR_TXNOFRM 0x00000200 /* No frame transmited (transmit timeout)*/ +#define AR5K_IMR_TXEOL 0x00000400 /* Empty TX descriptor*/ +#define AR5K_IMR_TXURN 0x00000800 /* Transmit FIFO underrun*/ +#define AR5K_IMR_MIB 0x00001000 /* Update MIB counters*/ +#define AR5K_IMR_SWI 0x00002000 /* Software interrupt */ +#define AR5K_IMR_RXPHY 0x00004000 /* PHY error*/ +#define AR5K_IMR_RXKCM 0x00008000 /* RX Key cache miss */ +#define AR5K_IMR_SWBA 0x00010000 /* Software beacon alert*/ +#define AR5K_IMR_BRSSI 0x00020000 /* Beacon rssi below threshold (?) */ +#define AR5K_IMR_BMISS 0x00040000 /* Beacon missed*/ +#define AR5K_IMR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */ +#define AR5K_IMR_BNR 0x00100000 /* Beacon not ready [5211+] */ +#define AR5K_IMR_MCABT 0x00100000 /* Master Cycle Abort [5210] */ +#define AR5K_IMR_RXCHIRP 0x00200000 /* CHIRP Received [5212+]*/ +#define AR5K_IMR_SSERR 0x00200000 /* Signaled System Error [5210] */ +#define AR5K_IMR_DPERR 0x00400000 /* Det par Error (?) [5210] */ +#define AR5K_IMR_RXDOPPLER 0x00400000 /* Doppler chirp received [5212+] */ +#define AR5K_IMR_TIM 0x00800000 /* [5211+] */ +#define AR5K_IMR_BCNMISC 0x00800000 /* 'or' of TIM, CAB_END, DTIM_SYNC, BCN_TIMEOUT, + CAB_TIMEOUT and DTIM bits from SISR2 [5212+] */ +#define AR5K_IMR_GPIO 0x01000000 /* GPIO (rf kill)*/ +#define AR5K_IMR_QCBRORN 0x02000000 /* QCU CBR overrun (?) [5211+] */ +#define AR5K_IMR_QCBRURN 0x04000000 /* QCU CBR underrun (?) [5211+] */ +#define AR5K_IMR_QTRIG 0x08000000 /* QCU scheduling trigger [5211+] */ + +/* + * Secondary interrupt mask registers [5211+] (0 - 4) + */ +#define AR5K_SIMR0 0x00a4 /* Register Address [5211+] */ +#define AR5K_SIMR0_QCU_TXOK 0x000003ff /* Mask for QCU_TXOK */ +#define AR5K_SIMR0_QCU_TXOK_S 0 +#define AR5K_SIMR0_QCU_TXDESC 0x03ff0000 /* Mask for QCU_TXDESC */ +#define AR5K_SIMR0_QCU_TXDESC_S 16 + +#define AR5K_SIMR1 0x00a8 /* Register Address [5211+] */ +#define AR5K_SIMR1_QCU_TXERR 0x000003ff /* Mask for QCU_TXERR */ +#define AR5K_SIMR1_QCU_TXERR_S 0 +#define AR5K_SIMR1_QCU_TXEOL 0x03ff0000 /* Mask for QCU_TXEOL */ +#define AR5K_SIMR1_QCU_TXEOL_S 16 + +#define AR5K_SIMR2 0x00ac /* Register Address [5211+] */ +#define AR5K_SIMR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */ +#define AR5K_SIMR2_QCU_TXURN_S 0 +#define AR5K_SIMR2_MCABT 0x00100000 /* Master Cycle Abort */ +#define AR5K_SIMR2_SSERR 0x00200000 /* Signaled System Error */ +#define AR5K_SIMR2_DPERR 0x00400000 /* Bus parity error */ +#define AR5K_SIMR2_TIM 0x01000000 /* [5212+] */ +#define AR5K_SIMR2_CAB_END 0x02000000 /* [5212+] */ +#define AR5K_SIMR2_DTIM_SYNC 0x04000000 /* DTIM Sync lost [5212+] */ +#define AR5K_SIMR2_BCN_TIMEOUT 0x08000000 /* Beacon Timeout [5212+] */ +#define AR5K_SIMR2_CAB_TIMEOUT 0x10000000 /* CAB Timeout [5212+] */ +#define AR5K_SIMR2_DTIM 0x20000000 /* [5212+] */ +#define AR5K_SIMR2_TSFOOR 0x80000000 /* TSF OOR (?) */ + +#define AR5K_SIMR3 0x00b0 /* Register Address [5211+] */ +#define AR5K_SIMR3_QCBRORN 0x000003ff /* Mask for QCBRORN */ +#define AR5K_SIMR3_QCBRORN_S 0 +#define AR5K_SIMR3_QCBRURN 0x03ff0000 /* Mask for QCBRURN */ +#define AR5K_SIMR3_QCBRURN_S 16 + +#define AR5K_SIMR4 0x00b4 /* Register Address [5211+] */ +#define AR5K_SIMR4_QTRIG 0x000003ff /* Mask for QTRIG */ +#define AR5K_SIMR4_QTRIG_S 0 + +/* + * DMA Debug registers 0-7 + * 0xe0 - 0xfc + */ + +/* + * Decompression mask registers [5212+] + */ +#define AR5K_DCM_ADDR 0x0400 /*Decompression mask address (index) */ +#define AR5K_DCM_DATA 0x0404 /*Decompression mask data */ + +/* + * Wake On Wireless pattern control register [5212+] + */ +#define AR5K_WOW_PCFG 0x0410 /* Register Address */ +#define AR5K_WOW_PCFG_PAT_MATCH_EN 0x00000001 /* Pattern match enable */ +#define AR5K_WOW_PCFG_LONG_FRAME_POL 0x00000002 /* Long frame policy */ +#define AR5K_WOW_PCFG_WOBMISS 0x00000004 /* Wake on bea(con) miss (?) */ +#define AR5K_WOW_PCFG_PAT_0_EN 0x00000100 /* Enable pattern 0 */ +#define AR5K_WOW_PCFG_PAT_1_EN 0x00000200 /* Enable pattern 1 */ +#define AR5K_WOW_PCFG_PAT_2_EN 0x00000400 /* Enable pattern 2 */ +#define AR5K_WOW_PCFG_PAT_3_EN 0x00000800 /* Enable pattern 3 */ +#define AR5K_WOW_PCFG_PAT_4_EN 0x00001000 /* Enable pattern 4 */ +#define AR5K_WOW_PCFG_PAT_5_EN 0x00002000 /* Enable pattern 5 */ + +/* + * Wake On Wireless pattern index register (?) [5212+] + */ +#define AR5K_WOW_PAT_IDX 0x0414 + +/* + * Wake On Wireless pattern data register [5212+] + */ +#define AR5K_WOW_PAT_DATA 0x0418 /* Register Address */ +#define AR5K_WOW_PAT_DATA_0_3_V 0x00000001 /* Pattern 0, 3 value */ +#define AR5K_WOW_PAT_DATA_1_4_V 0x00000100 /* Pattern 1, 4 value */ +#define AR5K_WOW_PAT_DATA_2_5_V 0x00010000 /* Pattern 2, 5 value */ +#define AR5K_WOW_PAT_DATA_0_3_M 0x01000000 /* Pattern 0, 3 mask */ +#define AR5K_WOW_PAT_DATA_1_4_M 0x04000000 /* Pattern 1, 4 mask */ +#define AR5K_WOW_PAT_DATA_2_5_M 0x10000000 /* Pattern 2, 5 mask */ + +/* + * Decompression configuration registers [5212+] + */ +#define AR5K_DCCFG 0x0420 /* Register Address */ +#define AR5K_DCCFG_GLOBAL_EN 0x00000001 /* Enable decompression on all queues */ +#define AR5K_DCCFG_BYPASS_EN 0x00000002 /* Bypass decompression */ +#define AR5K_DCCFG_BCAST_EN 0x00000004 /* Enable decompression for bcast frames */ +#define AR5K_DCCFG_MCAST_EN 0x00000008 /* Enable decompression for mcast frames */ + +/* + * Compression configuration registers [5212+] + */ +#define AR5K_CCFG 0x0600 /* Register Address */ +#define AR5K_CCFG_WINDOW_SIZE 0x00000007 /* Compression window size */ +#define AR5K_CCFG_CPC_EN 0x00000008 /* Enable performance counters */ + +#define AR5K_CCFG_CCU 0x0604 /* Register Address */ +#define AR5K_CCFG_CCU_CUP_EN 0x00000001 /* CCU Catchup enable */ +#define AR5K_CCFG_CCU_CREDIT 0x00000002 /* CCU Credit (field) */ +#define AR5K_CCFG_CCU_CD_THRES 0x00000080 /* CCU Cyc(lic?) debt threshold (field) */ +#define AR5K_CCFG_CCU_CUP_LCNT 0x00010000 /* CCU Catchup lit(?) count */ +#define AR5K_CCFG_CCU_INIT 0x00100200 /* Initial value during reset */ + +/* + * Compression performance counter registers [5212+] + */ +#define AR5K_CPC0 0x0610 /* Compression performance counter 0 */ +#define AR5K_CPC1 0x0614 /* Compression performance counter 1*/ +#define AR5K_CPC2 0x0618 /* Compression performance counter 2 */ +#define AR5K_CPC3 0x061c /* Compression performance counter 3 */ +#define AR5K_CPCOVF 0x0620 /* Compression performance overflow */ + + +/* + * Queue control unit (QCU) registers [5211+] + * + * Card has 12 TX Queues but i see that only 0-9 are used (?) + * both in binary HAL (see ah.h) and ar5k. Each queue has it's own + * TXDP at addresses 0x0800 - 0x082c, a CBR (Constant Bit Rate) + * configuration register (0x08c0 - 0x08ec), a ready time configuration + * register (0x0900 - 0x092c), a misc configuration register (0x09c0 - + * 0x09ec) and a status register (0x0a00 - 0x0a2c). We also have some + * global registers, QCU transmit enable/disable and "one shot arm (?)" + * set/clear, which contain status for all queues (we shift by 1 for each + * queue). To access these registers easily we define some macros here + * that are used inside HAL. For more infos check out *_tx_queue functs. + */ + +/* + * Generic QCU Register access macros + */ +#define AR5K_QUEUE_REG(_r, _q) (((_q) << 2) + _r) +#define AR5K_QCU_GLOBAL_READ(_r, _q) (AR5K_REG_READ(_r) & (1 << _q)) +#define AR5K_QCU_GLOBAL_WRITE(_r, _q) AR5K_REG_WRITE(_r, (1 << _q)) + +/* + * QCU Transmit descriptor pointer registers + */ +#define AR5K_QCU_TXDP_BASE 0x0800 /* Register Address - Queue0 TXDP */ +#define AR5K_QUEUE_TXDP(_q) AR5K_QUEUE_REG(AR5K_QCU_TXDP_BASE, _q) + +/* + * QCU Transmit enable register + */ +#define AR5K_QCU_TXE 0x0840 +#define AR5K_ENABLE_QUEUE(_q) AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXE, _q) +#define AR5K_QUEUE_ENABLED(_q) AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXE, _q) + +/* + * QCU Transmit disable register + */ +#define AR5K_QCU_TXD 0x0880 +#define AR5K_DISABLE_QUEUE(_q) AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXD, _q) +#define AR5K_QUEUE_DISABLED(_q) AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXD, _q) + +/* + * QCU Constant Bit Rate configuration registers + */ +#define AR5K_QCU_CBRCFG_BASE 0x08c0 /* Register Address - Queue0 CBRCFG */ +#define AR5K_QCU_CBRCFG_INTVAL 0x00ffffff /* CBR Interval mask */ +#define AR5K_QCU_CBRCFG_INTVAL_S 0 +#define AR5K_QCU_CBRCFG_ORN_THRES 0xff000000 /* CBR overrun threshold mask */ +#define AR5K_QCU_CBRCFG_ORN_THRES_S 24 +#define AR5K_QUEUE_CBRCFG(_q) AR5K_QUEUE_REG(AR5K_QCU_CBRCFG_BASE, _q) + +/* + * QCU Ready time configuration registers + */ +#define AR5K_QCU_RDYTIMECFG_BASE 0x0900 /* Register Address - Queue0 RDYTIMECFG */ +#define AR5K_QCU_RDYTIMECFG_INTVAL 0x00ffffff /* Ready time interval mask */ +#define AR5K_QCU_RDYTIMECFG_INTVAL_S 0 +#define AR5K_QCU_RDYTIMECFG_ENABLE 0x01000000 /* Ready time enable mask */ +#define AR5K_QUEUE_RDYTIMECFG(_q) AR5K_QUEUE_REG(AR5K_QCU_RDYTIMECFG_BASE, _q) + +/* + * QCU one shot arm set registers + */ +#define AR5K_QCU_ONESHOTARM_SET 0x0940 /* Register Address -QCU "one shot arm set (?)" */ +#define AR5K_QCU_ONESHOTARM_SET_M 0x0000ffff + +/* + * QCU one shot arm clear registers + */ +#define AR5K_QCU_ONESHOTARM_CLEAR 0x0980 /* Register Address -QCU "one shot arm clear (?)" */ +#define AR5K_QCU_ONESHOTARM_CLEAR_M 0x0000ffff + +/* + * QCU misc registers + */ +#define AR5K_QCU_MISC_BASE 0x09c0 /* Register Address -Queue0 MISC */ +#define AR5K_QCU_MISC_FRSHED_M 0x0000000f /* Frame sheduling mask */ +#define AR5K_QCU_MISC_FRSHED_ASAP 0 /* ASAP */ +#define AR5K_QCU_MISC_FRSHED_CBR 1 /* Constant Bit Rate */ +#define AR5K_QCU_MISC_FRSHED_DBA_GT 2 /* DMA Beacon alert gated */ +#define AR5K_QCU_MISC_FRSHED_TIM_GT 3 /* TIMT gated */ +#define AR5K_QCU_MISC_FRSHED_BCN_SENT_GT 4 /* Beacon sent gated */ +#define AR5K_QCU_MISC_ONESHOT_ENABLE 0x00000010 /* Oneshot enable */ +#define AR5K_QCU_MISC_CBREXP_DIS 0x00000020 /* Disable CBR expired counter (normal queue) */ +#define AR5K_QCU_MISC_CBREXP_BCN_DIS 0x00000040 /* Disable CBR expired counter (beacon queue) */ +#define AR5K_QCU_MISC_BCN_ENABLE 0x00000080 /* Enable Beacon use */ +#define AR5K_QCU_MISC_CBR_THRES_ENABLE 0x00000100 /* CBR expired threshold enabled */ +#define AR5K_QCU_MISC_RDY_VEOL_POLICY 0x00000200 /* TXE reset when RDYTIME expired or VEOL */ +#define AR5K_QCU_MISC_CBR_RESET_CNT 0x00000400 /* CBR threshold (counter) reset */ +#define AR5K_QCU_MISC_DCU_EARLY 0x00000800 /* DCU early termination */ +#define AR5K_QCU_MISC_DCU_CMP_EN 0x00001000 /* Enable frame compression */ +#define AR5K_QUEUE_MISC(_q) AR5K_QUEUE_REG(AR5K_QCU_MISC_BASE, _q) + + +/* + * QCU status registers + */ +#define AR5K_QCU_STS_BASE 0x0a00 /* Register Address - Queue0 STS */ +#define AR5K_QCU_STS_FRMPENDCNT 0x00000003 /* Frames pending counter */ +#define AR5K_QCU_STS_CBREXPCNT 0x0000ff00 /* CBR expired counter */ +#define AR5K_QUEUE_STATUS(_q) AR5K_QUEUE_REG(AR5K_QCU_STS_BASE, _q) + +/* + * QCU ready time shutdown register + */ +#define AR5K_QCU_RDYTIMESHDN 0x0a40 +#define AR5K_QCU_RDYTIMESHDN_M 0x000003ff + +/* + * QCU compression buffer base registers [5212+] + */ +#define AR5K_QCU_CBB_SELECT 0x0b00 +#define AR5K_QCU_CBB_ADDR 0x0b04 +#define AR5K_QCU_CBB_ADDR_S 9 + +/* + * QCU compression buffer configuration register [5212+] + * (buffer size) + */ +#define AR5K_QCU_CBCFG 0x0b08 + + + +/* + * Distributed Coordination Function (DCF) control unit (DCU) + * registers [5211+] + * + * These registers control the various characteristics of each queue + * for 802.11e (WME) combatibility so they go together with + * QCU registers in pairs. For each queue we have a QCU mask register, + * (0x1000 - 0x102c), a local-IFS settings register (0x1040 - 0x106c), + * a retry limit register (0x1080 - 0x10ac), a channel time register + * (0x10c0 - 0x10ec), a misc-settings register (0x1100 - 0x112c) and + * a sequence number register (0x1140 - 0x116c). It seems that "global" + * registers here afect all queues (see use of DCU_GBL_IFS_SLOT in ar5k). + * We use the same macros here for easier register access. + * + */ + +/* + * DCU QCU mask registers + */ +#define AR5K_DCU_QCUMASK_BASE 0x1000 /* Register Address -Queue0 DCU_QCUMASK */ +#define AR5K_DCU_QCUMASK_M 0x000003ff +#define AR5K_QUEUE_QCUMASK(_q) AR5K_QUEUE_REG(AR5K_DCU_QCUMASK_BASE, _q) + +/* + * DCU local Inter Frame Space settings register + */ +#define AR5K_DCU_LCL_IFS_BASE 0x1040 /* Register Address -Queue0 DCU_LCL_IFS */ +#define AR5K_DCU_LCL_IFS_CW_MIN 0x000003ff /* Minimum Contention Window */ +#define AR5K_DCU_LCL_IFS_CW_MIN_S 0 +#define AR5K_DCU_LCL_IFS_CW_MAX 0x000ffc00 /* Maximum Contention Window */ +#define AR5K_DCU_LCL_IFS_CW_MAX_S 10 +#define AR5K_DCU_LCL_IFS_AIFS 0x0ff00000 /* Arbitrated Interframe Space */ +#define AR5K_DCU_LCL_IFS_AIFS_S 20 +#define AR5K_DCU_LCL_IFS_AIFS_MAX 0xfc /* Anything above that can cause DCU to hang */ +#define AR5K_QUEUE_DFS_LOCAL_IFS(_q) AR5K_QUEUE_REG(AR5K_DCU_LCL_IFS_BASE, _q) + +/* + * DCU retry limit registers + */ +#define AR5K_DCU_RETRY_LMT_BASE 0x1080 /* Register Address -Queue0 DCU_RETRY_LMT */ +#define AR5K_DCU_RETRY_LMT_SH_RETRY 0x0000000f /* Short retry limit mask */ +#define AR5K_DCU_RETRY_LMT_SH_RETRY_S 0 +#define AR5K_DCU_RETRY_LMT_LG_RETRY 0x000000f0 /* Long retry limit mask */ +#define AR5K_DCU_RETRY_LMT_LG_RETRY_S 4 +#define AR5K_DCU_RETRY_LMT_SSH_RETRY 0x00003f00 /* Station short retry limit mask (?) */ +#define AR5K_DCU_RETRY_LMT_SSH_RETRY_S 8 +#define AR5K_DCU_RETRY_LMT_SLG_RETRY 0x000fc000 /* Station long retry limit mask (?) */ +#define AR5K_DCU_RETRY_LMT_SLG_RETRY_S 14 +#define AR5K_QUEUE_DFS_RETRY_LIMIT(_q) AR5K_QUEUE_REG(AR5K_DCU_RETRY_LMT_BASE, _q) + +/* + * DCU channel time registers + */ +#define AR5K_DCU_CHAN_TIME_BASE 0x10c0 /* Register Address -Queue0 DCU_CHAN_TIME */ +#define AR5K_DCU_CHAN_TIME_DUR 0x000fffff /* Channel time duration */ +#define AR5K_DCU_CHAN_TIME_DUR_S 0 +#define AR5K_DCU_CHAN_TIME_ENABLE 0x00100000 /* Enable channel time */ +#define AR5K_QUEUE_DFS_CHANNEL_TIME(_q) AR5K_QUEUE_REG(AR5K_DCU_CHAN_TIME_BASE, _q) + +/* + * DCU misc registers [5211+] + * + * Note: Arbiter lockout control controls the + * behaviour on low priority queues when we have multiple queues + * with pending frames. Intra-frame lockout means we wait until + * the queue's current frame transmits (with post frame backoff and bursting) + * before we transmit anything else and global lockout means we + * wait for the whole queue to finish before higher priority queues + * can transmit (this is used on beacon and CAB queues). + * No lockout means there is no special handling. + */ +#define AR5K_DCU_MISC_BASE 0x1100 /* Register Address -Queue0 DCU_MISC */ +#define AR5K_DCU_MISC_BACKOFF 0x0000003f /* Mask for backoff threshold */ +#define AR5K_DCU_MISC_ETS_RTS_POL 0x00000040 /* End of transmission series + station RTS/data failure count + reset policy (?) */ +#define AR5K_DCU_MISC_ETS_CW_POL 0x00000080 /* End of transmission series + CW reset policy */ +#define AR5K_DCU_MISC_FRAG_WAIT 0x00000100 /* Wait for next fragment */ +#define AR5K_DCU_MISC_BACKOFF_FRAG 0x00000200 /* Enable backoff while bursting */ +#define AR5K_DCU_MISC_HCFPOLL_ENABLE 0x00000800 /* CF - Poll enable */ +#define AR5K_DCU_MISC_BACKOFF_PERSIST 0x00001000 /* Persistent backoff */ +#define AR5K_DCU_MISC_FRMPRFTCH_ENABLE 0x00002000 /* Enable frame pre-fetch */ +#define AR5K_DCU_MISC_VIRTCOL 0x0000c000 /* Mask for Virtual Collision (?) */ +#define AR5K_DCU_MISC_VIRTCOL_NORMAL 0 +#define AR5K_DCU_MISC_VIRTCOL_IGNORE 1 +#define AR5K_DCU_MISC_BCN_ENABLE 0x00010000 /* Enable Beacon use */ +#define AR5K_DCU_MISC_ARBLOCK_CTL 0x00060000 /* Arbiter lockout control mask */ +#define AR5K_DCU_MISC_ARBLOCK_CTL_S 17 +#define AR5K_DCU_MISC_ARBLOCK_CTL_NONE 0 /* No arbiter lockout */ +#define AR5K_DCU_MISC_ARBLOCK_CTL_INTFRM 1 /* Intra-frame lockout */ +#define AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL 2 /* Global lockout */ +#define AR5K_DCU_MISC_ARBLOCK_IGNORE 0x00080000 /* Ignore Arbiter lockout */ +#define AR5K_DCU_MISC_SEQ_NUM_INCR_DIS 0x00100000 /* Disable sequence number increment */ +#define AR5K_DCU_MISC_POST_FR_BKOFF_DIS 0x00200000 /* Disable post-frame backoff */ +#define AR5K_DCU_MISC_VIRT_COLL_POLICY 0x00400000 /* Virtual Collision cw policy */ +#define AR5K_DCU_MISC_BLOWN_IFS_POLICY 0x00800000 /* Blown IFS policy (?) */ +#define AR5K_DCU_MISC_SEQNUM_CTL 0x01000000 /* Sequence number control (?) */ +#define AR5K_QUEUE_DFS_MISC(_q) AR5K_QUEUE_REG(AR5K_DCU_MISC_BASE, _q) + +/* + * DCU frame sequence number registers + */ +#define AR5K_DCU_SEQNUM_BASE 0x1140 +#define AR5K_DCU_SEQNUM_M 0x00000fff +#define AR5K_QUEUE_DCU_SEQNUM(_q) AR5K_QUEUE_REG(AR5K_DCU_SEQNUM_BASE, _q) + +/* + * DCU global IFS SIFS register + */ +#define AR5K_DCU_GBL_IFS_SIFS 0x1030 +#define AR5K_DCU_GBL_IFS_SIFS_M 0x0000ffff + +/* + * DCU global IFS slot interval register + */ +#define AR5K_DCU_GBL_IFS_SLOT 0x1070 +#define AR5K_DCU_GBL_IFS_SLOT_M 0x0000ffff + +/* + * DCU global IFS EIFS register + */ +#define AR5K_DCU_GBL_IFS_EIFS 0x10b0 +#define AR5K_DCU_GBL_IFS_EIFS_M 0x0000ffff + +/* + * DCU global IFS misc register + * + * LFSR stands for Linear Feedback Shift Register + * and it's used for generating pseudo-random + * number sequences. + * + * (If i understand corectly, random numbers are + * used for idle sensing -multiplied with cwmin/max etc-) + */ +#define AR5K_DCU_GBL_IFS_MISC 0x10f0 /* Register Address */ +#define AR5K_DCU_GBL_IFS_MISC_LFSR_SLICE 0x00000007 /* LFSR Slice Select */ +#define AR5K_DCU_GBL_IFS_MISC_TURBO_MODE 0x00000008 /* Turbo mode */ +#define AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC 0x000003f0 /* SIFS Duration mask */ +#define AR5K_DCU_GBL_IFS_MISC_USEC_DUR 0x000ffc00 /* USEC Duration mask */ +#define AR5K_DCU_GBL_IFS_MISC_USEC_DUR_S 10 +#define AR5K_DCU_GBL_IFS_MISC_DCU_ARB_DELAY 0x00300000 /* DCU Arbiter delay mask */ +#define AR5K_DCU_GBL_IFS_MISC_SIFS_CNT_RST 0x00400000 /* SIFS cnt reset policy (?) */ +#define AR5K_DCU_GBL_IFS_MISC_AIFS_CNT_RST 0x00800000 /* AIFS cnt reset policy (?) */ +#define AR5K_DCU_GBL_IFS_MISC_RND_LFSR_SL_DIS 0x01000000 /* Disable random LFSR slice */ + +/* + * DCU frame prefetch control register + */ +#define AR5K_DCU_FP 0x1230 /* Register Address */ +#define AR5K_DCU_FP_NOBURST_DCU_EN 0x00000001 /* Enable non-burst prefetch on DCU (?) */ +#define AR5K_DCU_FP_NOBURST_EN 0x00000010 /* Enable non-burst prefetch (?) */ +#define AR5K_DCU_FP_BURST_DCU_EN 0x00000020 /* Enable burst prefetch on DCU (?) */ + +/* + * DCU transmit pause control/status register + */ +#define AR5K_DCU_TXP 0x1270 /* Register Address */ +#define AR5K_DCU_TXP_M 0x000003ff /* Tx pause mask */ +#define AR5K_DCU_TXP_STATUS 0x00010000 /* Tx pause status */ + +/* + * DCU transmit filter table 0 (32 entries) + * each entry contains a 32bit slice of the + * 128bit tx filter for each DCU (4 slices per DCU) + */ +#define AR5K_DCU_TX_FILTER_0_BASE 0x1038 +#define AR5K_DCU_TX_FILTER_0(_n) (AR5K_DCU_TX_FILTER_0_BASE + (_n * 64)) + +/* + * DCU transmit filter table 1 (16 entries) + */ +#define AR5K_DCU_TX_FILTER_1_BASE 0x103c +#define AR5K_DCU_TX_FILTER_1(_n) (AR5K_DCU_TX_FILTER_1_BASE + (_n * 64)) + +/* + * DCU clear transmit filter register + */ +#define AR5K_DCU_TX_FILTER_CLR 0x143c + +/* + * DCU set transmit filter register + */ +#define AR5K_DCU_TX_FILTER_SET 0x147c + +/* + * Reset control register + */ +#define AR5K_RESET_CTL 0x4000 /* Register Address */ +#define AR5K_RESET_CTL_PCU 0x00000001 /* Protocol Control Unit reset */ +#define AR5K_RESET_CTL_DMA 0x00000002 /* DMA (Rx/Tx) reset [5210] */ +#define AR5K_RESET_CTL_BASEBAND 0x00000002 /* Baseband reset [5211+] */ +#define AR5K_RESET_CTL_MAC 0x00000004 /* MAC reset (PCU+Baseband ?) [5210] */ +#define AR5K_RESET_CTL_PHY 0x00000008 /* PHY reset [5210] */ +#define AR5K_RESET_CTL_PCI 0x00000010 /* PCI Core reset (interrupts etc) */ + +/* + * Sleep control register + */ +#define AR5K_SLEEP_CTL 0x4004 /* Register Address */ +#define AR5K_SLEEP_CTL_SLDUR 0x0000ffff /* Sleep duration mask */ +#define AR5K_SLEEP_CTL_SLDUR_S 0 +#define AR5K_SLEEP_CTL_SLE 0x00030000 /* Sleep enable mask */ +#define AR5K_SLEEP_CTL_SLE_S 16 +#define AR5K_SLEEP_CTL_SLE_WAKE 0x00000000 /* Force chip awake */ +#define AR5K_SLEEP_CTL_SLE_SLP 0x00010000 /* Force chip sleep */ +#define AR5K_SLEEP_CTL_SLE_ALLOW 0x00020000 /* Normal sleep policy */ +#define AR5K_SLEEP_CTL_SLE_UNITS 0x00000008 /* [5211+] */ +#define AR5K_SLEEP_CTL_DUR_TIM_POL 0x00040000 /* Sleep duration timing policy */ +#define AR5K_SLEEP_CTL_DUR_WRITE_POL 0x00080000 /* Sleep duration write policy */ +#define AR5K_SLEEP_CTL_SLE_POL 0x00100000 /* Sleep policy mode */ + +/* + * Interrupt pending register + */ +#define AR5K_INTPEND 0x4008 +#define AR5K_INTPEND_M 0x00000001 + +/* + * Sleep force register + */ +#define AR5K_SFR 0x400c +#define AR5K_SFR_EN 0x00000001 + +/* + * PCI configuration register + * TODO: Fix LED stuff + */ +#define AR5K_PCICFG 0x4010 /* Register Address */ +#define AR5K_PCICFG_EEAE 0x00000001 /* Eeprom access enable [5210] */ +#define AR5K_PCICFG_SLEEP_CLOCK_EN 0x00000002 /* Enable sleep clock */ +#define AR5K_PCICFG_CLKRUNEN 0x00000004 /* CLKRUN enable [5211+] */ +#define AR5K_PCICFG_EESIZE 0x00000018 /* Mask for EEPROM size [5211+] */ +#define AR5K_PCICFG_EESIZE_S 3 +#define AR5K_PCICFG_EESIZE_4K 0 /* 4K */ +#define AR5K_PCICFG_EESIZE_8K 1 /* 8K */ +#define AR5K_PCICFG_EESIZE_16K 2 /* 16K */ +#define AR5K_PCICFG_EESIZE_FAIL 3 /* Failed to get size [5211+] */ +#define AR5K_PCICFG_LED 0x00000060 /* Led status [5211+] */ +#define AR5K_PCICFG_LED_NONE 0x00000000 /* Default [5211+] */ +#define AR5K_PCICFG_LED_PEND 0x00000020 /* Scan / Auth pending */ +#define AR5K_PCICFG_LED_ASSOC 0x00000040 /* Associated */ +#define AR5K_PCICFG_BUS_SEL 0x00000380 /* Mask for "bus select" [5211+] (?) */ +#define AR5K_PCICFG_CBEFIX_DIS 0x00000400 /* Disable CBE fix */ +#define AR5K_PCICFG_SL_INTEN 0x00000800 /* Enable interrupts when asleep */ +#define AR5K_PCICFG_LED_BCTL 0x00001000 /* Led blink (?) [5210] */ +#define AR5K_PCICFG_RETRY_FIX 0x00001000 /* Enable pci core retry fix */ +#define AR5K_PCICFG_SL_INPEN 0x00002000 /* Sleep even whith pending interrupts*/ +#define AR5K_PCICFG_SPWR_DN 0x00010000 /* Mask for power status */ +#define AR5K_PCICFG_LEDMODE 0x000e0000 /* Ledmode [5211+] */ +#define AR5K_PCICFG_LEDMODE_PROP 0x00000000 /* Blink on standard traffic [5211+] */ +#define AR5K_PCICFG_LEDMODE_PROM 0x00020000 /* Default mode (blink on any traffic) [5211+] */ +#define AR5K_PCICFG_LEDMODE_PWR 0x00040000 /* Some other blinking mode (?) [5211+] */ +#define AR5K_PCICFG_LEDMODE_RAND 0x00060000 /* Random blinking (?) [5211+] */ +#define AR5K_PCICFG_LEDBLINK 0x00700000 /* Led blink rate */ +#define AR5K_PCICFG_LEDBLINK_S 20 +#define AR5K_PCICFG_LEDSLOW 0x00800000 /* Slowest led blink rate [5211+] */ +#define AR5K_PCICFG_LEDSTATE \ + (AR5K_PCICFG_LED | AR5K_PCICFG_LEDMODE | \ + AR5K_PCICFG_LEDBLINK | AR5K_PCICFG_LEDSLOW) +#define AR5K_PCICFG_SLEEP_CLOCK_RATE 0x03000000 /* Sleep clock rate */ +#define AR5K_PCICFG_SLEEP_CLOCK_RATE_S 24 + +/* + * "General Purpose Input/Output" (GPIO) control register + * + * I'm not sure about this but after looking at the code + * for all chipsets here is what i got. + * + * We have 6 GPIOs (pins), each GPIO has 4 modes (2 bits) + * Mode 0 -> always input + * Mode 1 -> output when GPIODO for this GPIO is set to 0 + * Mode 2 -> output when GPIODO for this GPIO is set to 1 + * Mode 3 -> always output + * + * For more infos check out get_gpio/set_gpio and + * set_gpio_input/set_gpio_output functs. + * For more infos on gpio interrupt check out set_gpio_intr. + */ +#define AR5K_NUM_GPIO 6 + +#define AR5K_GPIOCR 0x4014 /* Register Address */ +#define AR5K_GPIOCR_INT_ENA 0x00008000 /* Enable GPIO interrupt */ +#define AR5K_GPIOCR_INT_SELL 0x00000000 /* Generate interrupt when pin is low */ +#define AR5K_GPIOCR_INT_SELH 0x00010000 /* Generate interrupt when pin is high */ +#define AR5K_GPIOCR_IN(n) (0 << ((n) * 2)) /* Mode 0 for pin n */ +#define AR5K_GPIOCR_OUT0(n) (1 << ((n) * 2)) /* Mode 1 for pin n */ +#define AR5K_GPIOCR_OUT1(n) (2 << ((n) * 2)) /* Mode 2 for pin n */ +#define AR5K_GPIOCR_OUT(n) (3 << ((n) * 2)) /* Mode 3 for pin n */ +#define AR5K_GPIOCR_INT_SEL(n) ((n) << 12) /* Interrupt for GPIO pin n */ + +/* + * "General Purpose Input/Output" (GPIO) data output register + */ +#define AR5K_GPIODO 0x4018 + +/* + * "General Purpose Input/Output" (GPIO) data input register + */ +#define AR5K_GPIODI 0x401c +#define AR5K_GPIODI_M 0x0000002f + +/* + * Silicon revision register + */ +#define AR5K_SREV 0x4020 /* Register Address */ +#define AR5K_SREV_REV 0x0000000f /* Mask for revision */ +#define AR5K_SREV_REV_S 0 +#define AR5K_SREV_VER 0x000000ff /* Mask for version */ +#define AR5K_SREV_VER_S 4 + +/* + * TXE write posting register + */ +#define AR5K_TXEPOST 0x4028 + +/* + * QCU sleep mask + */ +#define AR5K_QCU_SLEEP_MASK 0x402c + +/* 0x4068 is compression buffer configuration + * register on 5414 and pm configuration register + * on 5424 and newer pci-e chips. */ + +/* + * Compression buffer configuration + * register (enable/disable) [5414] + */ +#define AR5K_5414_CBCFG 0x4068 +#define AR5K_5414_CBCFG_BUF_DIS 0x10 /* Disable buffer */ + +/* + * PCI-E Power managment configuration + * and status register [5424+] + */ +#define AR5K_PCIE_PM_CTL 0x4068 /* Register address */ +/* Only 5424 */ +#define AR5K_PCIE_PM_CTL_L1_WHEN_D2 0x00000001 /* enable PCIe core enter L1 + when d2_sleep_en is asserted */ +#define AR5K_PCIE_PM_CTL_L0_L0S_CLEAR 0x00000002 /* Clear L0 and L0S counters */ +#define AR5K_PCIE_PM_CTL_L0_L0S_EN 0x00000004 /* Start L0 nd L0S counters */ +#define AR5K_PCIE_PM_CTL_LDRESET_EN 0x00000008 /* Enable reset when link goes + down */ +/* Wake On Wireless */ +#define AR5K_PCIE_PM_CTL_PME_EN 0x00000010 /* PME Enable */ +#define AR5K_PCIE_PM_CTL_AUX_PWR_DET 0x00000020 /* Aux power detect */ +#define AR5K_PCIE_PM_CTL_PME_CLEAR 0x00000040 /* Clear PME */ +#define AR5K_PCIE_PM_CTL_PSM_D0 0x00000080 +#define AR5K_PCIE_PM_CTL_PSM_D1 0x00000100 +#define AR5K_PCIE_PM_CTL_PSM_D2 0x00000200 +#define AR5K_PCIE_PM_CTL_PSM_D3 0x00000400 + +/* + * PCI-E Workaround enable register + */ +#define AR5K_PCIE_WAEN 0x407c + +/* + * PCI-E Serializer/Desirializer + * registers + */ +#define AR5K_PCIE_SERDES 0x4080 +#define AR5K_PCIE_SERDES_RESET 0x4084 + +/*====EEPROM REGISTERS====*/ + +/* + * EEPROM access registers + * + * Here we got a difference between 5210/5211-12 + * read data register for 5210 is at 0x6800 and + * status register is at 0x6c00. There is also + * no eeprom command register on 5210 and the + * offsets are different. + * + * To read eeprom data for a specific offset: + * 5210 - enable eeprom access (AR5K_PCICFG_EEAE) + * read AR5K_EEPROM_BASE +(4 * offset) + * check the eeprom status register + * and read eeprom data register. + * + * 5211 - write offset to AR5K_EEPROM_BASE + * 5212 write AR5K_EEPROM_CMD_READ on AR5K_EEPROM_CMD + * check the eeprom status register + * and read eeprom data register. + * + * To write eeprom data for a specific offset: + * 5210 - enable eeprom access (AR5K_PCICFG_EEAE) + * write data to AR5K_EEPROM_BASE +(4 * offset) + * check the eeprom status register + * 5211 - write AR5K_EEPROM_CMD_RESET on AR5K_EEPROM_CMD + * 5212 write offset to AR5K_EEPROM_BASE + * write data to data register + * write AR5K_EEPROM_CMD_WRITE on AR5K_EEPROM_CMD + * check the eeprom status register + * + * For more infos check eeprom_* functs and the ar5k.c + * file posted in madwifi-devel mailing list. + * http://sourceforge.net/mailarchive/message.php?msg_id=8966525 + * + */ +#define AR5K_EEPROM_BASE 0x6000 + +/* + * EEPROM data register + */ +#define AR5K_EEPROM_DATA_5211 0x6004 +#define AR5K_EEPROM_DATA_5210 0x6800 +#define AR5K_EEPROM_DATA (ah->ah_version == AR5K_AR5210 ? \ + AR5K_EEPROM_DATA_5210 : AR5K_EEPROM_DATA_5211) + +/* + * EEPROM command register + */ +#define AR5K_EEPROM_CMD 0x6008 /* Register Addres */ +#define AR5K_EEPROM_CMD_READ 0x00000001 /* EEPROM read */ +#define AR5K_EEPROM_CMD_WRITE 0x00000002 /* EEPROM write */ +#define AR5K_EEPROM_CMD_RESET 0x00000004 /* EEPROM reset */ + +/* + * EEPROM status register + */ +#define AR5K_EEPROM_STAT_5210 0x6c00 /* Register Address [5210] */ +#define AR5K_EEPROM_STAT_5211 0x600c /* Register Address [5211+] */ +#define AR5K_EEPROM_STATUS (ah->ah_version == AR5K_AR5210 ? \ + AR5K_EEPROM_STAT_5210 : AR5K_EEPROM_STAT_5211) +#define AR5K_EEPROM_STAT_RDERR 0x00000001 /* EEPROM read failed */ +#define AR5K_EEPROM_STAT_RDDONE 0x00000002 /* EEPROM read successful */ +#define AR5K_EEPROM_STAT_WRERR 0x00000004 /* EEPROM write failed */ +#define AR5K_EEPROM_STAT_WRDONE 0x00000008 /* EEPROM write successful */ + +/* + * EEPROM config register + */ +#define AR5K_EEPROM_CFG 0x6010 /* Register Addres */ +#define AR5K_EEPROM_CFG_SIZE 0x00000003 /* Size determination override */ +#define AR5K_EEPROM_CFG_SIZE_AUTO 0 +#define AR5K_EEPROM_CFG_SIZE_4KBIT 1 +#define AR5K_EEPROM_CFG_SIZE_8KBIT 2 +#define AR5K_EEPROM_CFG_SIZE_16KBIT 3 +#define AR5K_EEPROM_CFG_WR_WAIT_DIS 0x00000004 /* Disable write wait */ +#define AR5K_EEPROM_CFG_CLK_RATE 0x00000018 /* Clock rate */ +#define AR5K_EEPROM_CFG_CLK_RATE_S 3 +#define AR5K_EEPROM_CFG_CLK_RATE_156KHZ 0 +#define AR5K_EEPROM_CFG_CLK_RATE_312KHZ 1 +#define AR5K_EEPROM_CFG_CLK_RATE_625KHZ 2 +#define AR5K_EEPROM_CFG_PROT_KEY 0x00ffff00 /* Protection key */ +#define AR5K_EEPROM_CFG_PROT_KEY_S 8 +#define AR5K_EEPROM_CFG_LIND_EN 0x01000000 /* Enable length indicator (?) */ + + +/* + * TODO: Wake On Wireless registers + * Range 0x7000 - 0x7ce0 + */ + +/* + * Protocol Control Unit (PCU) registers + */ +/* + * Used for checking initial register writes + * during channel reset (see reset func) + */ +#define AR5K_PCU_MIN 0x8000 +#define AR5K_PCU_MAX 0x8fff + +/* + * First station id register (Lower 32 bits of MAC address) + */ +#define AR5K_STA_ID0 0x8000 +#define AR5K_STA_ID0_ARRD_L32 0xffffffff + +/* + * Second station id register (Upper 16 bits of MAC address + PCU settings) + */ +#define AR5K_STA_ID1 0x8004 /* Register Address */ +#define AR5K_STA_ID1_ADDR_U16 0x0000ffff /* Upper 16 bits of MAC addres */ +#define AR5K_STA_ID1_AP 0x00010000 /* Set AP mode */ +#define AR5K_STA_ID1_ADHOC 0x00020000 /* Set Ad-Hoc mode */ +#define AR5K_STA_ID1_PWR_SV 0x00040000 /* Power save reporting */ +#define AR5K_STA_ID1_NO_KEYSRCH 0x00080000 /* No key search */ +#define AR5K_STA_ID1_NO_PSPOLL 0x00100000 /* No power save polling [5210] */ +#define AR5K_STA_ID1_PCF_5211 0x00100000 /* Enable PCF on [5211+] */ +#define AR5K_STA_ID1_PCF_5210 0x00200000 /* Enable PCF on [5210]*/ +#define AR5K_STA_ID1_PCF (ah->ah_version == AR5K_AR5210 ? \ + AR5K_STA_ID1_PCF_5210 : AR5K_STA_ID1_PCF_5211) +#define AR5K_STA_ID1_DEFAULT_ANTENNA 0x00200000 /* Use default antenna */ +#define AR5K_STA_ID1_DESC_ANTENNA 0x00400000 /* Update antenna from descriptor */ +#define AR5K_STA_ID1_RTS_DEF_ANTENNA 0x00800000 /* Use default antenna for RTS */ +#define AR5K_STA_ID1_ACKCTS_6MB 0x01000000 /* Use 6Mbit/s for ACK/CTS */ +#define AR5K_STA_ID1_BASE_RATE_11B 0x02000000 /* Use 11b base rate for ACK/CTS [5211+] */ +#define AR5K_STA_ID1_SELFGEN_DEF_ANT 0x04000000 /* Use def. antenna for self generated frames */ +#define AR5K_STA_ID1_CRYPT_MIC_EN 0x08000000 /* Enable MIC */ +#define AR5K_STA_ID1_KEYSRCH_MODE 0x10000000 /* Look up key when key id != 0 */ +#define AR5K_STA_ID1_PRESERVE_SEQ_NUM 0x20000000 /* Preserve sequence number */ +#define AR5K_STA_ID1_CBCIV_ENDIAN 0x40000000 /* ??? */ +#define AR5K_STA_ID1_KEYSRCH_MCAST 0x80000000 /* Do key cache search for mcast frames */ + +#define AR5K_STA_ID1_ANTENNA_SETTINGS (AR5K_STA_ID1_DEFAULT_ANTENNA | \ + AR5K_STA_ID1_DESC_ANTENNA | \ + AR5K_STA_ID1_RTS_DEF_ANTENNA | \ + AR5K_STA_ID1_SELFGEN_DEF_ANT) + +/* + * First BSSID register (MAC address, lower 32bits) + */ +#define AR5K_BSS_ID0 0x8008 + +/* + * Second BSSID register (MAC address in upper 16 bits) + * + * AID: Association ID + */ +#define AR5K_BSS_ID1 0x800c +#define AR5K_BSS_ID1_AID 0xffff0000 +#define AR5K_BSS_ID1_AID_S 16 + +/* + * Backoff slot time register + */ +#define AR5K_SLOT_TIME 0x8010 + +/* + * ACK/CTS timeout register + */ +#define AR5K_TIME_OUT 0x8014 /* Register Address */ +#define AR5K_TIME_OUT_ACK 0x00001fff /* ACK timeout mask */ +#define AR5K_TIME_OUT_ACK_S 0 +#define AR5K_TIME_OUT_CTS 0x1fff0000 /* CTS timeout mask */ +#define AR5K_TIME_OUT_CTS_S 16 + +/* + * RSSI threshold register + */ +#define AR5K_RSSI_THR 0x8018 /* Register Address */ +#define AR5K_RSSI_THR_M 0x000000ff /* Mask for RSSI threshold [5211+] */ +#define AR5K_RSSI_THR_BMISS_5210 0x00000700 /* Mask for Beacon Missed threshold [5210] */ +#define AR5K_RSSI_THR_BMISS_5210_S 8 +#define AR5K_RSSI_THR_BMISS_5211 0x0000ff00 /* Mask for Beacon Missed threshold [5211+] */ +#define AR5K_RSSI_THR_BMISS_5211_S 8 +#define AR5K_RSSI_THR_BMISS (ah->ah_version == AR5K_AR5210 ? \ + AR5K_RSSI_THR_BMISS_5210 : AR5K_RSSI_THR_BMISS_5211) +#define AR5K_RSSI_THR_BMISS_S 8 + +/* + * 5210 has more PCU registers because there is no QCU/DCU + * so queue parameters are set here, this way a lot common + * registers have different address for 5210. To make things + * easier we define a macro based on ah->ah_version for common + * registers with different addresses and common flags. + */ + +/* + * Retry limit register + * + * Retry limit register for 5210 (no QCU/DCU so it's done in PCU) + */ +#define AR5K_NODCU_RETRY_LMT 0x801c /* Register Address */ +#define AR5K_NODCU_RETRY_LMT_SH_RETRY 0x0000000f /* Short retry limit mask */ +#define AR5K_NODCU_RETRY_LMT_SH_RETRY_S 0 +#define AR5K_NODCU_RETRY_LMT_LG_RETRY 0x000000f0 /* Long retry mask */ +#define AR5K_NODCU_RETRY_LMT_LG_RETRY_S 4 +#define AR5K_NODCU_RETRY_LMT_SSH_RETRY 0x00003f00 /* Station short retry limit mask */ +#define AR5K_NODCU_RETRY_LMT_SSH_RETRY_S 8 +#define AR5K_NODCU_RETRY_LMT_SLG_RETRY 0x000fc000 /* Station long retry limit mask */ +#define AR5K_NODCU_RETRY_LMT_SLG_RETRY_S 14 +#define AR5K_NODCU_RETRY_LMT_CW_MIN 0x3ff00000 /* Minimum contention window mask */ +#define AR5K_NODCU_RETRY_LMT_CW_MIN_S 20 + +/* + * Transmit latency register + */ +#define AR5K_USEC_5210 0x8020 /* Register Address [5210] */ +#define AR5K_USEC_5211 0x801c /* Register Address [5211+] */ +#define AR5K_USEC (ah->ah_version == AR5K_AR5210 ? \ + AR5K_USEC_5210 : AR5K_USEC_5211) +#define AR5K_USEC_1 0x0000007f /* clock cycles for 1us */ +#define AR5K_USEC_1_S 0 +#define AR5K_USEC_32 0x00003f80 /* clock cycles for 1us while on 32Mhz clock */ +#define AR5K_USEC_32_S 7 +#define AR5K_USEC_TX_LATENCY_5211 0x007fc000 +#define AR5K_USEC_TX_LATENCY_5211_S 14 +#define AR5K_USEC_RX_LATENCY_5211 0x1f800000 +#define AR5K_USEC_RX_LATENCY_5211_S 23 +#define AR5K_USEC_TX_LATENCY_5210 0x000fc000 /* also for 5311 */ +#define AR5K_USEC_TX_LATENCY_5210_S 14 +#define AR5K_USEC_RX_LATENCY_5210 0x03f00000 /* also for 5311 */ +#define AR5K_USEC_RX_LATENCY_5210_S 20 + +/* + * PCU beacon control register + */ +#define AR5K_BEACON_5210 0x8024 /*Register Address [5210] */ +#define AR5K_BEACON_5211 0x8020 /*Register Address [5211+] */ +#define AR5K_BEACON (ah->ah_version == AR5K_AR5210 ? \ + AR5K_BEACON_5210 : AR5K_BEACON_5211) +#define AR5K_BEACON_PERIOD 0x0000ffff /* Mask for beacon period */ +#define AR5K_BEACON_PERIOD_S 0 +#define AR5K_BEACON_TIM 0x007f0000 /* Mask for TIM offset */ +#define AR5K_BEACON_TIM_S 16 +#define AR5K_BEACON_ENABLE 0x00800000 /* Enable beacons */ +#define AR5K_BEACON_RESET_TSF 0x01000000 /* Force TSF reset */ + +/* + * CFP period register + */ +#define AR5K_CFP_PERIOD_5210 0x8028 +#define AR5K_CFP_PERIOD_5211 0x8024 +#define AR5K_CFP_PERIOD (ah->ah_version == AR5K_AR5210 ? \ + AR5K_CFP_PERIOD_5210 : AR5K_CFP_PERIOD_5211) + +/* + * Next beacon time register + */ +#define AR5K_TIMER0_5210 0x802c +#define AR5K_TIMER0_5211 0x8028 +#define AR5K_TIMER0 (ah->ah_version == AR5K_AR5210 ? \ + AR5K_TIMER0_5210 : AR5K_TIMER0_5211) + +/* + * Next DMA beacon alert register + */ +#define AR5K_TIMER1_5210 0x8030 +#define AR5K_TIMER1_5211 0x802c +#define AR5K_TIMER1 (ah->ah_version == AR5K_AR5210 ? \ + AR5K_TIMER1_5210 : AR5K_TIMER1_5211) + +/* + * Next software beacon alert register + */ +#define AR5K_TIMER2_5210 0x8034 +#define AR5K_TIMER2_5211 0x8030 +#define AR5K_TIMER2 (ah->ah_version == AR5K_AR5210 ? \ + AR5K_TIMER2_5210 : AR5K_TIMER2_5211) + +/* + * Next ATIM window time register + */ +#define AR5K_TIMER3_5210 0x8038 +#define AR5K_TIMER3_5211 0x8034 +#define AR5K_TIMER3 (ah->ah_version == AR5K_AR5210 ? \ + AR5K_TIMER3_5210 : AR5K_TIMER3_5211) + + +/* + * 5210 First inter frame spacing register (IFS) + */ +#define AR5K_IFS0 0x8040 +#define AR5K_IFS0_SIFS 0x000007ff +#define AR5K_IFS0_SIFS_S 0 +#define AR5K_IFS0_DIFS 0x007ff800 +#define AR5K_IFS0_DIFS_S 11 + +/* + * 5210 Second inter frame spacing register (IFS) + */ +#define AR5K_IFS1 0x8044 +#define AR5K_IFS1_PIFS 0x00000fff +#define AR5K_IFS1_PIFS_S 0 +#define AR5K_IFS1_EIFS 0x03fff000 +#define AR5K_IFS1_EIFS_S 12 +#define AR5K_IFS1_CS_EN 0x04000000 + + +/* + * CFP duration register + */ +#define AR5K_CFP_DUR_5210 0x8048 +#define AR5K_CFP_DUR_5211 0x8038 +#define AR5K_CFP_DUR (ah->ah_version == AR5K_AR5210 ? \ + AR5K_CFP_DUR_5210 : AR5K_CFP_DUR_5211) + +/* + * Receive filter register + */ +#define AR5K_RX_FILTER_5210 0x804c /* Register Address [5210] */ +#define AR5K_RX_FILTER_5211 0x803c /* Register Address [5211+] */ +#define AR5K_RX_FILTER (ah->ah_version == AR5K_AR5210 ? \ + AR5K_RX_FILTER_5210 : AR5K_RX_FILTER_5211) +#define AR5K_RX_FILTER_UCAST 0x00000001 /* Don't filter unicast frames */ +#define AR5K_RX_FILTER_MCAST 0x00000002 /* Don't filter multicast frames */ +#define AR5K_RX_FILTER_BCAST 0x00000004 /* Don't filter broadcast frames */ +#define AR5K_RX_FILTER_CONTROL 0x00000008 /* Don't filter control frames */ +#define AR5K_RX_FILTER_BEACON 0x00000010 /* Don't filter beacon frames */ +#define AR5K_RX_FILTER_PROM 0x00000020 /* Set promiscuous mode */ +#define AR5K_RX_FILTER_XRPOLL 0x00000040 /* Don't filter XR poll frame [5212+] */ +#define AR5K_RX_FILTER_PROBEREQ 0x00000080 /* Don't filter probe requests [5212+] */ +#define AR5K_RX_FILTER_PHYERR_5212 0x00000100 /* Don't filter phy errors [5212+] */ +#define AR5K_RX_FILTER_RADARERR_5212 0x00000200 /* Don't filter phy radar errors [5212+] */ +#define AR5K_RX_FILTER_PHYERR_5211 0x00000040 /* [5211] */ +#define AR5K_RX_FILTER_RADARERR_5211 0x00000080 /* [5211] */ +#define AR5K_RX_FILTER_PHYERR \ + ((ah->ah_version == AR5K_AR5211 ? \ + AR5K_RX_FILTER_PHYERR_5211 : AR5K_RX_FILTER_PHYERR_5212)) +#define AR5K_RX_FILTER_RADARERR \ + ((ah->ah_version == AR5K_AR5211 ? \ + AR5K_RX_FILTER_RADARERR_5211 : AR5K_RX_FILTER_RADARERR_5212)) + +/* + * Multicast filter register (lower 32 bits) + */ +#define AR5K_MCAST_FILTER0_5210 0x8050 +#define AR5K_MCAST_FILTER0_5211 0x8040 +#define AR5K_MCAST_FILTER0 (ah->ah_version == AR5K_AR5210 ? \ + AR5K_MCAST_FILTER0_5210 : AR5K_MCAST_FILTER0_5211) + +/* + * Multicast filter register (higher 16 bits) + */ +#define AR5K_MCAST_FILTER1_5210 0x8054 +#define AR5K_MCAST_FILTER1_5211 0x8044 +#define AR5K_MCAST_FILTER1 (ah->ah_version == AR5K_AR5210 ? \ + AR5K_MCAST_FILTER1_5210 : AR5K_MCAST_FILTER1_5211) + + +/* + * Transmit mask register (lower 32 bits) [5210] + */ +#define AR5K_TX_MASK0 0x8058 + +/* + * Transmit mask register (higher 16 bits) [5210] + */ +#define AR5K_TX_MASK1 0x805c + +/* + * Clear transmit mask [5210] + */ +#define AR5K_CLR_TMASK 0x8060 + +/* + * Trigger level register (before transmission) [5210] + */ +#define AR5K_TRIG_LVL 0x8064 + + +/* + * PCU control register + * + * Only DIS_RX is used in the code, the rest i guess are + * for tweaking/diagnostics. + */ +#define AR5K_DIAG_SW_5210 0x8068 /* Register Address [5210] */ +#define AR5K_DIAG_SW_5211 0x8048 /* Register Address [5211+] */ +#define AR5K_DIAG_SW (ah->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_5210 : AR5K_DIAG_SW_5211) +#define AR5K_DIAG_SW_DIS_WEP_ACK 0x00000001 /* Disable ACKs if WEP key is invalid */ +#define AR5K_DIAG_SW_DIS_ACK 0x00000002 /* Disable ACKs */ +#define AR5K_DIAG_SW_DIS_CTS 0x00000004 /* Disable CTSs */ +#define AR5K_DIAG_SW_DIS_ENC 0x00000008 /* Disable encryption */ +#define AR5K_DIAG_SW_DIS_DEC 0x00000010 /* Disable decryption */ +#define AR5K_DIAG_SW_DIS_TX 0x00000020 /* Disable transmit [5210] */ +#define AR5K_DIAG_SW_DIS_RX_5210 0x00000040 /* Disable recieve */ +#define AR5K_DIAG_SW_DIS_RX_5211 0x00000020 +#define AR5K_DIAG_SW_DIS_RX (ah->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_DIS_RX_5210 : AR5K_DIAG_SW_DIS_RX_5211) +#define AR5K_DIAG_SW_LOOP_BACK_5210 0x00000080 /* Loopback (i guess it goes with DIS_TX) [5210] */ +#define AR5K_DIAG_SW_LOOP_BACK_5211 0x00000040 +#define AR5K_DIAG_SW_LOOP_BACK (ah->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_LOOP_BACK_5210 : AR5K_DIAG_SW_LOOP_BACK_5211) +#define AR5K_DIAG_SW_CORR_FCS_5210 0x00000100 /* Corrupted FCS */ +#define AR5K_DIAG_SW_CORR_FCS_5211 0x00000080 +#define AR5K_DIAG_SW_CORR_FCS (ah->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_CORR_FCS_5210 : AR5K_DIAG_SW_CORR_FCS_5211) +#define AR5K_DIAG_SW_CHAN_INFO_5210 0x00000200 /* Dump channel info */ +#define AR5K_DIAG_SW_CHAN_INFO_5211 0x00000100 +#define AR5K_DIAG_SW_CHAN_INFO (ah->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_CHAN_INFO_5210 : AR5K_DIAG_SW_CHAN_INFO_5211) +#define AR5K_DIAG_SW_EN_SCRAM_SEED_5210 0x00000400 /* Enable fixed scrambler seed */ +#define AR5K_DIAG_SW_EN_SCRAM_SEED_5211 0x00000200 +#define AR5K_DIAG_SW_EN_SCRAM_SEED (ah->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_EN_SCRAM_SEED_5210 : AR5K_DIAG_SW_EN_SCRAM_SEED_5211) +#define AR5K_DIAG_SW_ECO_ENABLE 0x00000400 /* [5211+] */ +#define AR5K_DIAG_SW_SCVRAM_SEED 0x0003f800 /* [5210] */ +#define AR5K_DIAG_SW_SCRAM_SEED_M 0x0001fc00 /* Scrambler seed mask */ +#define AR5K_DIAG_SW_SCRAM_SEED_S 10 +#define AR5K_DIAG_SW_DIS_SEQ_INC 0x00040000 /* Disable seqnum increment (?)[5210] */ +#define AR5K_DIAG_SW_FRAME_NV0_5210 0x00080000 +#define AR5K_DIAG_SW_FRAME_NV0_5211 0x00020000 /* Accept frames of non-zero protocol number */ +#define AR5K_DIAG_SW_FRAME_NV0 (ah->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_FRAME_NV0_5210 : AR5K_DIAG_SW_FRAME_NV0_5211) +#define AR5K_DIAG_SW_OBSPT_M 0x000c0000 /* Observation point select (?) */ +#define AR5K_DIAG_SW_OBSPT_S 18 +#define AR5K_DIAG_SW_RX_CLEAR_HIGH 0x0010000 /* Force RX Clear high */ +#define AR5K_DIAG_SW_IGNORE_CARR_SENSE 0x0020000 /* Ignore virtual carrier sense */ +#define AR5K_DIAG_SW_CHANEL_IDLE_HIGH 0x0040000 /* Force channel idle high */ +#define AR5K_DIAG_SW_PHEAR_ME 0x0080000 /* ??? */ + +/* + * TSF (clock) register (lower 32 bits) + */ +#define AR5K_TSF_L32_5210 0x806c +#define AR5K_TSF_L32_5211 0x804c +#define AR5K_TSF_L32 (ah->ah_version == AR5K_AR5210 ? \ + AR5K_TSF_L32_5210 : AR5K_TSF_L32_5211) + +/* + * TSF (clock) register (higher 32 bits) + */ +#define AR5K_TSF_U32_5210 0x8070 +#define AR5K_TSF_U32_5211 0x8050 +#define AR5K_TSF_U32 (ah->ah_version == AR5K_AR5210 ? \ + AR5K_TSF_U32_5210 : AR5K_TSF_U32_5211) + +/* + * Last beacon timestamp register (Read Only) + */ +#define AR5K_LAST_TSTP 0x8080 + +/* + * ADDAC test register [5211+] + */ +#define AR5K_ADDAC_TEST 0x8054 /* Register Address */ +#define AR5K_ADDAC_TEST_TXCONT 0x00000001 /* Test continuous tx */ +#define AR5K_ADDAC_TEST_TST_MODE 0x00000002 /* Test mode */ +#define AR5K_ADDAC_TEST_LOOP_EN 0x00000004 /* Enable loop */ +#define AR5K_ADDAC_TEST_LOOP_LEN 0x00000008 /* Loop length (field) */ +#define AR5K_ADDAC_TEST_USE_U8 0x00004000 /* Use upper 8 bits */ +#define AR5K_ADDAC_TEST_MSB 0x00008000 /* State of MSB */ +#define AR5K_ADDAC_TEST_TRIG_SEL 0x00010000 /* Trigger select */ +#define AR5K_ADDAC_TEST_TRIG_PTY 0x00020000 /* Trigger polarity */ +#define AR5K_ADDAC_TEST_RXCONT 0x00040000 /* Continuous capture */ +#define AR5K_ADDAC_TEST_CAPTURE 0x00080000 /* Begin capture */ +#define AR5K_ADDAC_TEST_TST_ARM 0x00100000 /* ARM rx buffer for capture */ + +/* + * Default antenna register [5211+] + */ +#define AR5K_DEFAULT_ANTENNA 0x8058 + +/* + * Frame control QoS mask register (?) [5211+] + * (FC_QOS_MASK) + */ +#define AR5K_FRAME_CTL_QOSM 0x805c + +/* + * Seq mask register (?) [5211+] + */ +#define AR5K_SEQ_MASK 0x8060 + +/* + * Retry count register [5210] + */ +#define AR5K_RETRY_CNT 0x8084 /* Register Address [5210] */ +#define AR5K_RETRY_CNT_SSH 0x0000003f /* Station short retry count (?) */ +#define AR5K_RETRY_CNT_SLG 0x00000fc0 /* Station long retry count (?) */ + +/* + * Back-off status register [5210] + */ +#define AR5K_BACKOFF 0x8088 /* Register Address [5210] */ +#define AR5K_BACKOFF_CW 0x000003ff /* Backoff Contention Window (?) */ +#define AR5K_BACKOFF_CNT 0x03ff0000 /* Backoff count (?) */ + + + +/* + * NAV register (current) + */ +#define AR5K_NAV_5210 0x808c +#define AR5K_NAV_5211 0x8084 +#define AR5K_NAV (ah->ah_version == AR5K_AR5210 ? \ + AR5K_NAV_5210 : AR5K_NAV_5211) + +/* + * RTS success register + */ +#define AR5K_RTS_OK_5210 0x8090 +#define AR5K_RTS_OK_5211 0x8088 +#define AR5K_RTS_OK (ah->ah_version == AR5K_AR5210 ? \ + AR5K_RTS_OK_5210 : AR5K_RTS_OK_5211) + +/* + * RTS failure register + */ +#define AR5K_RTS_FAIL_5210 0x8094 +#define AR5K_RTS_FAIL_5211 0x808c +#define AR5K_RTS_FAIL (ah->ah_version == AR5K_AR5210 ? \ + AR5K_RTS_FAIL_5210 : AR5K_RTS_FAIL_5211) + +/* + * ACK failure register + */ +#define AR5K_ACK_FAIL_5210 0x8098 +#define AR5K_ACK_FAIL_5211 0x8090 +#define AR5K_ACK_FAIL (ah->ah_version == AR5K_AR5210 ? \ + AR5K_ACK_FAIL_5210 : AR5K_ACK_FAIL_5211) + +/* + * FCS failure register + */ +#define AR5K_FCS_FAIL_5210 0x809c +#define AR5K_FCS_FAIL_5211 0x8094 +#define AR5K_FCS_FAIL (ah->ah_version == AR5K_AR5210 ? \ + AR5K_FCS_FAIL_5210 : AR5K_FCS_FAIL_5211) + +/* + * Beacon count register + */ +#define AR5K_BEACON_CNT_5210 0x80a0 +#define AR5K_BEACON_CNT_5211 0x8098 +#define AR5K_BEACON_CNT (ah->ah_version == AR5K_AR5210 ? \ + AR5K_BEACON_CNT_5210 : AR5K_BEACON_CNT_5211) + + +/*===5212 Specific PCU registers===*/ + +/* + * Transmit power control register + */ +#define AR5K_TPC 0x80e8 +#define AR5K_TPC_ACK 0x0000003f /* ack frames */ +#define AR5K_TPC_ACK_S 0 +#define AR5K_TPC_CTS 0x00003f00 /* cts frames */ +#define AR5K_TPC_CTS_S 8 +#define AR5K_TPC_CHIRP 0x003f0000 /* chirp frames */ +#define AR5K_TPC_CHIRP_S 16 +#define AR5K_TPC_DOPPLER 0x0f000000 /* doppler chirp span */ +#define AR5K_TPC_DOPPLER_S 24 + +/* + * XR (eXtended Range) mode register + */ +#define AR5K_XRMODE 0x80c0 /* Register Address */ +#define AR5K_XRMODE_POLL_TYPE_M 0x0000003f /* Mask for Poll type (?) */ +#define AR5K_XRMODE_POLL_TYPE_S 0 +#define AR5K_XRMODE_POLL_SUBTYPE_M 0x0000003c /* Mask for Poll subtype (?) */ +#define AR5K_XRMODE_POLL_SUBTYPE_S 2 +#define AR5K_XRMODE_POLL_WAIT_ALL 0x00000080 /* Wait for poll */ +#define AR5K_XRMODE_SIFS_DELAY 0x000fff00 /* Mask for SIFS delay */ +#define AR5K_XRMODE_FRAME_HOLD_M 0xfff00000 /* Mask for frame hold (?) */ +#define AR5K_XRMODE_FRAME_HOLD_S 20 + +/* + * XR delay register + */ +#define AR5K_XRDELAY 0x80c4 /* Register Address */ +#define AR5K_XRDELAY_SLOT_DELAY_M 0x0000ffff /* Mask for slot delay */ +#define AR5K_XRDELAY_SLOT_DELAY_S 0 +#define AR5K_XRDELAY_CHIRP_DELAY_M 0xffff0000 /* Mask for CHIRP data delay */ +#define AR5K_XRDELAY_CHIRP_DELAY_S 16 + +/* + * XR timeout register + */ +#define AR5K_XRTIMEOUT 0x80c8 /* Register Address */ +#define AR5K_XRTIMEOUT_CHIRP_M 0x0000ffff /* Mask for CHIRP timeout */ +#define AR5K_XRTIMEOUT_CHIRP_S 0 +#define AR5K_XRTIMEOUT_POLL_M 0xffff0000 /* Mask for Poll timeout */ +#define AR5K_XRTIMEOUT_POLL_S 16 + +/* + * XR chirp register + */ +#define AR5K_XRCHIRP 0x80cc /* Register Address */ +#define AR5K_XRCHIRP_SEND 0x00000001 /* Send CHIRP */ +#define AR5K_XRCHIRP_GAP 0xffff0000 /* Mask for CHIRP gap (?) */ + +/* + * XR stomp register + */ +#define AR5K_XRSTOMP 0x80d0 /* Register Address */ +#define AR5K_XRSTOMP_TX 0x00000001 /* Stomp Tx (?) */ +#define AR5K_XRSTOMP_RX 0x00000002 /* Stomp Rx (?) */ +#define AR5K_XRSTOMP_TX_RSSI 0x00000004 /* Stomp Tx RSSI (?) */ +#define AR5K_XRSTOMP_TX_BSSID 0x00000008 /* Stomp Tx BSSID (?) */ +#define AR5K_XRSTOMP_DATA 0x00000010 /* Stomp data (?)*/ +#define AR5K_XRSTOMP_RSSI_THRES 0x0000ff00 /* Mask for XR RSSI threshold */ + +/* + * First enhanced sleep register + */ +#define AR5K_SLEEP0 0x80d4 /* Register Address */ +#define AR5K_SLEEP0_NEXT_DTIM 0x0007ffff /* Mask for next DTIM (?) */ +#define AR5K_SLEEP0_NEXT_DTIM_S 0 +#define AR5K_SLEEP0_ASSUME_DTIM 0x00080000 /* Assume DTIM */ +#define AR5K_SLEEP0_ENH_SLEEP_EN 0x00100000 /* Enable enchanced sleep control */ +#define AR5K_SLEEP0_CABTO 0xff000000 /* Mask for CAB Time Out */ +#define AR5K_SLEEP0_CABTO_S 24 + +/* + * Second enhanced sleep register + */ +#define AR5K_SLEEP1 0x80d8 /* Register Address */ +#define AR5K_SLEEP1_NEXT_TIM 0x0007ffff /* Mask for next TIM (?) */ +#define AR5K_SLEEP1_NEXT_TIM_S 0 +#define AR5K_SLEEP1_BEACON_TO 0xff000000 /* Mask for Beacon Time Out */ +#define AR5K_SLEEP1_BEACON_TO_S 24 + +/* + * Third enhanced sleep register + */ +#define AR5K_SLEEP2 0x80dc /* Register Address */ +#define AR5K_SLEEP2_TIM_PER 0x0000ffff /* Mask for TIM period (?) */ +#define AR5K_SLEEP2_TIM_PER_S 0 +#define AR5K_SLEEP2_DTIM_PER 0xffff0000 /* Mask for DTIM period (?) */ +#define AR5K_SLEEP2_DTIM_PER_S 16 + +/* + * BSSID mask registers + */ +#define AR5K_BSS_IDM0 0x80e0 /* Upper bits */ +#define AR5K_BSS_IDM1 0x80e4 /* Lower bits */ + +/* + * TX power control (TPC) register + * + * XXX: PCDAC steps (0.5dbm) or DBM ? + * + */ +#define AR5K_TXPC 0x80e8 /* Register Address */ +#define AR5K_TXPC_ACK_M 0x0000003f /* ACK tx power */ +#define AR5K_TXPC_ACK_S 0 +#define AR5K_TXPC_CTS_M 0x00003f00 /* CTS tx power */ +#define AR5K_TXPC_CTS_S 8 +#define AR5K_TXPC_CHIRP_M 0x003f0000 /* CHIRP tx power */ +#define AR5K_TXPC_CHIRP_S 16 +#define AR5K_TXPC_DOPPLER 0x0f000000 /* Doppler chirp span (?) */ +#define AR5K_TXPC_DOPPLER_S 24 + +/* + * Profile count registers + */ +#define AR5K_PROFCNT_TX 0x80ec /* Tx count */ +#define AR5K_PROFCNT_RX 0x80f0 /* Rx count */ +#define AR5K_PROFCNT_RXCLR 0x80f4 /* Clear Rx count */ +#define AR5K_PROFCNT_CYCLE 0x80f8 /* Cycle count (?) */ + +/* + * Quiet period control registers + */ +#define AR5K_QUIET_CTL1 0x80fc /* Register Address */ +#define AR5K_QUIET_CTL1_NEXT_QT_TSF 0x0000ffff /* Next quiet period TSF (TU) */ +#define AR5K_QUIET_CTL1_NEXT_QT_TSF_S 0 +#define AR5K_QUIET_CTL1_QT_EN 0x00010000 /* Enable quiet period */ +#define AR5K_QUIET_CTL1_ACK_CTS_EN 0x00020000 /* Send ACK/CTS during quiet period */ + +#define AR5K_QUIET_CTL2 0x8100 /* Register Address */ +#define AR5K_QUIET_CTL2_QT_PER 0x0000ffff /* Mask for quiet period periodicity */ +#define AR5K_QUIET_CTL2_QT_PER_S 0 +#define AR5K_QUIET_CTL2_QT_DUR 0xffff0000 /* Mask for quiet period duration */ +#define AR5K_QUIET_CTL2_QT_DUR_S 16 + +/* + * TSF parameter register + */ +#define AR5K_TSF_PARM 0x8104 /* Register Address */ +#define AR5K_TSF_PARM_INC 0x000000ff /* Mask for TSF increment */ +#define AR5K_TSF_PARM_INC_S 0 + +/* + * QoS NOACK policy + */ +#define AR5K_QOS_NOACK 0x8108 /* Register Address */ +#define AR5K_QOS_NOACK_2BIT_VALUES 0x0000000f /* ??? */ +#define AR5K_QOS_NOACK_2BIT_VALUES_S 0 +#define AR5K_QOS_NOACK_BIT_OFFSET 0x00000070 /* ??? */ +#define AR5K_QOS_NOACK_BIT_OFFSET_S 4 +#define AR5K_QOS_NOACK_BYTE_OFFSET 0x00000180 /* ??? */ +#define AR5K_QOS_NOACK_BYTE_OFFSET_S 7 + +/* + * PHY error filter register + */ +#define AR5K_PHY_ERR_FIL 0x810c +#define AR5K_PHY_ERR_FIL_RADAR 0x00000020 /* Radar signal */ +#define AR5K_PHY_ERR_FIL_OFDM 0x00020000 /* OFDM false detect (ANI) */ +#define AR5K_PHY_ERR_FIL_CCK 0x02000000 /* CCK false detect (ANI) */ + +/* + * XR latency register + */ +#define AR5K_XRLAT_TX 0x8110 + +/* + * ACK SIFS register + */ +#define AR5K_ACKSIFS 0x8114 /* Register Address */ +#define AR5K_ACKSIFS_INC 0x00000000 /* ACK SIFS Increment (field) */ + +/* + * MIC QoS control register (?) + */ +#define AR5K_MIC_QOS_CTL 0x8118 /* Register Address */ +#define AR5K_MIC_QOS_CTL_OFF(_n) (1 << (_n * 2)) +#define AR5K_MIC_QOS_CTL_MQ_EN 0x00010000 /* Enable MIC QoS */ + +/* + * MIC QoS select register (?) + */ +#define AR5K_MIC_QOS_SEL 0x811c +#define AR5K_MIC_QOS_SEL_OFF(_n) (1 << (_n * 4)) + +/* + * Misc mode control register (?) + */ +#define AR5K_MISC_MODE 0x8120 /* Register Address */ +#define AR5K_MISC_MODE_FBSSID_MATCH 0x00000001 /* Force BSSID match */ +#define AR5K_MISC_MODE_ACKSIFS_MEM 0x00000002 /* ACK SIFS memory (?) */ +#define AR5K_MISC_MODE_COMBINED_MIC 0x00000004 /* use rx/tx MIC key */ +/* more bits */ + +/* + * OFDM Filter counter + */ +#define AR5K_OFDM_FIL_CNT 0x8124 + +/* + * CCK Filter counter + */ +#define AR5K_CCK_FIL_CNT 0x8128 + +/* + * PHY Error Counters (?) + */ +#define AR5K_PHYERR_CNT1 0x812c +#define AR5K_PHYERR_CNT1_MASK 0x8130 + +#define AR5K_PHYERR_CNT2 0x8134 +#define AR5K_PHYERR_CNT2_MASK 0x8138 + +/* + * TSF Threshold register (?) + */ +#define AR5K_TSF_THRES 0x813c + +/* + * TODO: Wake On Wireless registers + * Range: 0x8147 - 0x818c + */ + +/* + * Rate -> ACK SIFS mapping table (32 entries) + */ +#define AR5K_RATE_ACKSIFS_BASE 0x8680 /* Register Address */ +#define AR5K_RATE_ACKSIFS(_n) (AR5K_RATE_ACKSIFS_BSE + ((_n) << 2)) +#define AR5K_RATE_ACKSIFS_NORMAL 0x00000001 /* Normal SIFS (field) */ +#define AR5K_RATE_ACKSIFS_TURBO 0x00000400 /* Turbo SIFS (field) */ + +/* + * Rate -> duration mapping table (32 entries) + */ +#define AR5K_RATE_DUR_BASE 0x8700 +#define AR5K_RATE_DUR(_n) (AR5K_RATE_DUR_BASE + ((_n) << 2)) + +/* + * Rate -> db mapping table + * (8 entries, each one has 4 8bit fields) + */ +#define AR5K_RATE2DB_BASE 0x87c0 +#define AR5K_RATE2DB(_n) (AR5K_RATE2DB_BASE + ((_n) << 2)) + +/* + * db -> Rate mapping table + * (8 entries, each one has 4 8bit fields) + */ +#define AR5K_DB2RATE_BASE 0x87e0 +#define AR5K_DB2RATE(_n) (AR5K_DB2RATE_BASE + ((_n) << 2)) + +/*===5212 end===*/ + +/* + * Key table (WEP) register + */ +#define AR5K_KEYTABLE_0_5210 0x9000 +#define AR5K_KEYTABLE_0_5211 0x8800 +#define AR5K_KEYTABLE_5210(_n) (AR5K_KEYTABLE_0_5210 + ((_n) << 5)) +#define AR5K_KEYTABLE_5211(_n) (AR5K_KEYTABLE_0_5211 + ((_n) << 5)) +#define AR5K_KEYTABLE(_n) (ah->ah_version == AR5K_AR5210 ? \ + AR5K_KEYTABLE_5210(_n) : AR5K_KEYTABLE_5211(_n)) +#define AR5K_KEYTABLE_OFF(_n, x) (AR5K_KEYTABLE(_n) + (x << 2)) +#define AR5K_KEYTABLE_TYPE(_n) AR5K_KEYTABLE_OFF(_n, 5) +#define AR5K_KEYTABLE_TYPE_40 0x00000000 +#define AR5K_KEYTABLE_TYPE_104 0x00000001 +#define AR5K_KEYTABLE_TYPE_128 0x00000003 +#define AR5K_KEYTABLE_TYPE_TKIP 0x00000004 /* [5212+] */ +#define AR5K_KEYTABLE_TYPE_AES 0x00000005 /* [5211+] */ +#define AR5K_KEYTABLE_TYPE_CCM 0x00000006 /* [5212+] */ +#define AR5K_KEYTABLE_TYPE_NULL 0x00000007 /* [5211+] */ +#define AR5K_KEYTABLE_ANTENNA 0x00000008 /* [5212+] */ +#define AR5K_KEYTABLE_MAC0(_n) AR5K_KEYTABLE_OFF(_n, 6) +#define AR5K_KEYTABLE_MAC1(_n) AR5K_KEYTABLE_OFF(_n, 7) +#define AR5K_KEYTABLE_VALID 0x00008000 + +/* If key type is TKIP and MIC is enabled + * MIC key goes in offset entry + 64 */ +#define AR5K_KEYTABLE_MIC_OFFSET 64 + +/* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit + * WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit + * WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit + * + * Some vendors have introduced bigger WEP keys to address + * security vulnerabilities in WEP. This includes: + * + * WEP 232-bit = 232-bit entered key + 24 bit IV = 256-bit + * + * We can expand this if we find ar5k Atheros cards with a larger + * key table size. + */ +#define AR5K_KEYTABLE_SIZE_5210 64 +#define AR5K_KEYTABLE_SIZE_5211 128 +#define AR5K_KEYTABLE_SIZE (ah->ah_version == AR5K_AR5210 ? \ + AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211) + + +/*===PHY REGISTERS===*/ + +/* + * PHY registers start + */ +#define AR5K_PHY_BASE 0x9800 +#define AR5K_PHY(_n) (AR5K_PHY_BASE + ((_n) << 2)) + +/* + * TST_2 (Misc config parameters) + */ +#define AR5K_PHY_TST2 0x9800 /* Register Address */ +#define AR5K_PHY_TST2_TRIG_SEL 0x00000007 /* Trigger select (?)*/ +#define AR5K_PHY_TST2_TRIG 0x00000010 /* Trigger (?) */ +#define AR5K_PHY_TST2_CBUS_MODE 0x00000060 /* Cardbus mode (?) */ +#define AR5K_PHY_TST2_CLK32 0x00000400 /* CLK_OUT is CLK32 (32Khz external) */ +#define AR5K_PHY_TST2_CHANCOR_DUMP_EN 0x00000800 /* Enable Chancor dump (?) */ +#define AR5K_PHY_TST2_EVEN_CHANCOR_DUMP 0x00001000 /* Even Chancor dump (?) */ +#define AR5K_PHY_TST2_RFSILENT_EN 0x00002000 /* Enable RFSILENT */ +#define AR5K_PHY_TST2_ALT_RFDATA 0x00004000 /* Alternate RFDATA (5-2GHz switch ?) */ +#define AR5K_PHY_TST2_MINI_OBS_EN 0x00008000 /* Enable mini OBS (?) */ +#define AR5K_PHY_TST2_RX2_IS_RX5_INV 0x00010000 /* 2GHz rx path is the 5GHz path inverted (?) */ +#define AR5K_PHY_TST2_SLOW_CLK160 0x00020000 /* Slow CLK160 (?) */ +#define AR5K_PHY_TST2_AGC_OBS_SEL_3 0x00040000 /* AGC OBS Select 3 (?) */ +#define AR5K_PHY_TST2_BBB_OBS_SEL 0x00080000 /* BB OBS Select (field ?) */ +#define AR5K_PHY_TST2_ADC_OBS_SEL 0x00800000 /* ADC OBS Select (field ?) */ +#define AR5K_PHY_TST2_RX_CLR_SEL 0x08000000 /* RX Clear Select (?) */ +#define AR5K_PHY_TST2_FORCE_AGC_CLR 0x10000000 /* Force AGC clear (?) */ +#define AR5K_PHY_SHIFT_2GHZ 0x00004007 /* Used to access 2GHz radios */ +#define AR5K_PHY_SHIFT_5GHZ 0x00000007 /* Used to access 5GHz radios (default) */ + +/* + * PHY frame control register [5110] /turbo mode register [5111+] + * + * There is another frame control register for [5111+] + * at address 0x9944 (see below) but the 2 first flags + * are common here between 5110 frame control register + * and [5111+] turbo mode register, so this also works as + * a "turbo mode register" for 5110. We treat this one as + * a frame control register for 5110 below. + */ +#define AR5K_PHY_TURBO 0x9804 /* Register Address */ +#define AR5K_PHY_TURBO_MODE 0x00000001 /* Enable turbo mode */ +#define AR5K_PHY_TURBO_SHORT 0x00000002 /* Set short symbols to turbo mode */ +#define AR5K_PHY_TURBO_MIMO 0x00000004 /* Set turbo for mimo mimo */ + +/* + * PHY agility command register + * (aka TST_1) + */ +#define AR5K_PHY_AGC 0x9808 /* Register Address */ +#define AR5K_PHY_TST1 0x9808 +#define AR5K_PHY_AGC_DISABLE 0x08000000 /* Disable AGC to A2 (?)*/ +#define AR5K_PHY_TST1_TXHOLD 0x00003800 /* Set tx hold (?) */ +#define AR5K_PHY_TST1_TXSRC_SRC 0x00000002 /* Used with bit 7 (?) */ +#define AR5K_PHY_TST1_TXSRC_SRC_S 1 +#define AR5K_PHY_TST1_TXSRC_ALT 0x00000080 /* Set input to tsdac (?) */ +#define AR5K_PHY_TST1_TXSRC_ALT_S 7 + + +/* + * PHY timing register 3 [5112+] + */ +#define AR5K_PHY_TIMING_3 0x9814 +#define AR5K_PHY_TIMING_3_DSC_MAN 0xfffe0000 +#define AR5K_PHY_TIMING_3_DSC_MAN_S 17 +#define AR5K_PHY_TIMING_3_DSC_EXP 0x0001e000 +#define AR5K_PHY_TIMING_3_DSC_EXP_S 13 + +/* + * PHY chip revision register + */ +#define AR5K_PHY_CHIP_ID 0x9818 + +/* + * PHY activation register + */ +#define AR5K_PHY_ACT 0x981c /* Register Address */ +#define AR5K_PHY_ACT_ENABLE 0x00000001 /* Activate PHY */ +#define AR5K_PHY_ACT_DISABLE 0x00000002 /* Deactivate PHY */ + +/* + * PHY RF control registers + */ +#define AR5K_PHY_RF_CTL2 0x9824 /* Register Address */ +#define AR5K_PHY_RF_CTL2_TXF2TXD_START 0x0000000f /* TX frame to TX data start */ +#define AR5K_PHY_RF_CTL2_TXF2TXD_START_S 0 + +#define AR5K_PHY_RF_CTL3 0x9828 /* Register Address */ +#define AR5K_PHY_RF_CTL3_TXE2XLNA_ON 0x0000ff00 /* TX end to XLNA on */ +#define AR5K_PHY_RF_CTL3_TXE2XLNA_ON_S 8 + +#define AR5K_PHY_ADC_CTL 0x982c +#define AR5K_PHY_ADC_CTL_INBUFGAIN_OFF 0x00000003 +#define AR5K_PHY_ADC_CTL_INBUFGAIN_OFF_S 0 +#define AR5K_PHY_ADC_CTL_PWD_DAC_OFF 0x00002000 +#define AR5K_PHY_ADC_CTL_PWD_BAND_GAP_OFF 0x00004000 +#define AR5K_PHY_ADC_CTL_PWD_ADC_OFF 0x00008000 +#define AR5K_PHY_ADC_CTL_INBUFGAIN_ON 0x00030000 +#define AR5K_PHY_ADC_CTL_INBUFGAIN_ON_S 16 + +#define AR5K_PHY_RF_CTL4 0x9834 /* Register Address */ +#define AR5K_PHY_RF_CTL4_TXF2XPA_A_ON 0x00000001 /* TX frame to XPA A on (field) */ +#define AR5K_PHY_RF_CTL4_TXF2XPA_B_ON 0x00000100 /* TX frame to XPA B on (field) */ +#define AR5K_PHY_RF_CTL4_TXE2XPA_A_OFF 0x00010000 /* TX end to XPA A off (field) */ +#define AR5K_PHY_RF_CTL4_TXE2XPA_B_OFF 0x01000000 /* TX end to XPA B off (field) */ + +/* + * Pre-Amplifier control register + * (XPA -> external pre-amplifier) + */ +#define AR5K_PHY_PA_CTL 0x9838 /* Register Address */ +#define AR5K_PHY_PA_CTL_XPA_A_HI 0x00000001 /* XPA A high (?) */ +#define AR5K_PHY_PA_CTL_XPA_B_HI 0x00000002 /* XPA B high (?) */ +#define AR5K_PHY_PA_CTL_XPA_A_EN 0x00000004 /* Enable XPA A */ +#define AR5K_PHY_PA_CTL_XPA_B_EN 0x00000008 /* Enable XPA B */ + +/* + * PHY settling register + */ +#define AR5K_PHY_SETTLING 0x9844 /* Register Address */ +#define AR5K_PHY_SETTLING_AGC 0x0000007f /* AGC settling time */ +#define AR5K_PHY_SETTLING_AGC_S 0 +#define AR5K_PHY_SETTLING_SWITCH 0x00003f80 /* Switch settlig time */ +#define AR5K_PHY_SETTLING_SWITCH_S 7 + +/* + * PHY Gain registers + */ +#define AR5K_PHY_GAIN 0x9848 /* Register Address */ +#define AR5K_PHY_GAIN_TXRX_ATTEN 0x0003f000 /* TX-RX Attenuation */ +#define AR5K_PHY_GAIN_TXRX_ATTEN_S 12 +#define AR5K_PHY_GAIN_TXRX_RF_MAX 0x007c0000 +#define AR5K_PHY_GAIN_TXRX_RF_MAX_S 18 + +#define AR5K_PHY_GAIN_OFFSET 0x984c /* Register Address */ +#define AR5K_PHY_GAIN_OFFSET_RXTX_FLAG 0x00020000 /* RX-TX flag (?) */ + +/* + * Desired ADC/PGA size register + * (for more infos read ANI patent) + */ +#define AR5K_PHY_DESIRED_SIZE 0x9850 /* Register Address */ +#define AR5K_PHY_DESIRED_SIZE_ADC 0x000000ff /* ADC desired size */ +#define AR5K_PHY_DESIRED_SIZE_ADC_S 0 +#define AR5K_PHY_DESIRED_SIZE_PGA 0x0000ff00 /* PGA desired size */ +#define AR5K_PHY_DESIRED_SIZE_PGA_S 8 +#define AR5K_PHY_DESIRED_SIZE_TOT 0x0ff00000 /* Total desired size */ +#define AR5K_PHY_DESIRED_SIZE_TOT_S 20 + +/* + * PHY signal register + * (for more infos read ANI patent) + */ +#define AR5K_PHY_SIG 0x9858 /* Register Address */ +#define AR5K_PHY_SIG_FIRSTEP 0x0003f000 /* FIRSTEP */ +#define AR5K_PHY_SIG_FIRSTEP_S 12 +#define AR5K_PHY_SIG_FIRPWR 0x03fc0000 /* FIPWR */ +#define AR5K_PHY_SIG_FIRPWR_S 18 + +/* + * PHY coarse agility control register + * (for more infos read ANI patent) + */ +#define AR5K_PHY_AGCCOARSE 0x985c /* Register Address */ +#define AR5K_PHY_AGCCOARSE_LO 0x00007f80 /* AGC Coarse low */ +#define AR5K_PHY_AGCCOARSE_LO_S 7 +#define AR5K_PHY_AGCCOARSE_HI 0x003f8000 /* AGC Coarse high */ +#define AR5K_PHY_AGCCOARSE_HI_S 15 + +/* + * PHY agility control register + */ +#define AR5K_PHY_AGCCTL 0x9860 /* Register address */ +#define AR5K_PHY_AGCCTL_CAL 0x00000001 /* Enable PHY calibration */ +#define AR5K_PHY_AGCCTL_NF 0x00000002 /* Enable Noise Floor calibration */ +#define AR5K_PHY_AGCCTL_OFDM_DIV_DIS 0x00000008 /* Disable antenna diversity on OFDM modes */ +#define AR5K_PHY_AGCCTL_NF_EN 0x00008000 /* Enable nf calibration to happen (?) */ +#define AR5K_PHY_AGCTL_FLTR_CAL 0x00010000 /* Allow filter calibration (?) */ +#define AR5K_PHY_AGCCTL_NF_NOUPDATE 0x00020000 /* Don't update nf automaticaly */ + +/* + * PHY noise floor status register + */ +#define AR5K_PHY_NF 0x9864 /* Register address */ +#define AR5K_PHY_NF_M 0x000001ff /* Noise floor mask */ +#define AR5K_PHY_NF_ACTIVE 0x00000100 /* Noise floor calibration still active */ +#define AR5K_PHY_NF_RVAL(_n) (((_n) >> 19) & AR5K_PHY_NF_M) +#define AR5K_PHY_NF_AVAL(_n) (-((_n) ^ AR5K_PHY_NF_M) + 1) +#define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9)) +#define AR5K_PHY_NF_THRESH62 0x0007f000 /* Thresh62 -check ANI patent- (field) */ +#define AR5K_PHY_NF_THRESH62_S 12 +#define AR5K_PHY_NF_MINCCA_PWR 0x0ff80000 /* ??? */ +#define AR5K_PHY_NF_MINCCA_PWR_S 19 + +/* + * PHY ADC saturation register [5110] + */ +#define AR5K_PHY_ADCSAT 0x9868 +#define AR5K_PHY_ADCSAT_ICNT 0x0001f800 +#define AR5K_PHY_ADCSAT_ICNT_S 11 +#define AR5K_PHY_ADCSAT_THR 0x000007e0 +#define AR5K_PHY_ADCSAT_THR_S 5 + +/* + * PHY Weak ofdm signal detection threshold registers (ANI) [5212+] + */ + +/* High thresholds */ +#define AR5K_PHY_WEAK_OFDM_HIGH_THR 0x9868 +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT 0x0000001f +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT_S 0 +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M1 0x00fe0000 +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M1_S 17 +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2 0x7f000000 +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_S 24 + +/* Low thresholds */ +#define AR5K_PHY_WEAK_OFDM_LOW_THR 0x986c +#define AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN 0x00000001 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT 0x00003f00 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT_S 8 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M1 0x001fc000 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M1_S 14 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2 0x0fe00000 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_S 21 + + +/* + * PHY sleep registers [5112+] + */ +#define AR5K_PHY_SCR 0x9870 + +#define AR5K_PHY_SLMT 0x9874 +#define AR5K_PHY_SLMT_32MHZ 0x0000007f + +#define AR5K_PHY_SCAL 0x9878 +#define AR5K_PHY_SCAL_32MHZ 0x0000000e +#define AR5K_PHY_SCAL_32MHZ_2417 0x0000000a +#define AR5K_PHY_SCAL_32MHZ_HB63 0x00000032 + +/* + * PHY PLL (Phase Locked Loop) control register + */ +#define AR5K_PHY_PLL 0x987c +#define AR5K_PHY_PLL_20MHZ 0x00000013 /* For half rate (?) */ +/* 40MHz -> 5GHz band */ +#define AR5K_PHY_PLL_40MHZ_5211 0x00000018 +#define AR5K_PHY_PLL_40MHZ_5212 0x000000aa +#define AR5K_PHY_PLL_40MHZ_5413 0x00000004 +#define AR5K_PHY_PLL_40MHZ (ah->ah_version == AR5K_AR5211 ? \ + AR5K_PHY_PLL_40MHZ_5211 : AR5K_PHY_PLL_40MHZ_5212) +/* 44MHz -> 2.4GHz band */ +#define AR5K_PHY_PLL_44MHZ_5211 0x00000019 +#define AR5K_PHY_PLL_44MHZ_5212 0x000000ab +#define AR5K_PHY_PLL_44MHZ (ah->ah_version == AR5K_AR5211 ? \ + AR5K_PHY_PLL_44MHZ_5211 : AR5K_PHY_PLL_44MHZ_5212) + +#define AR5K_PHY_PLL_RF5111 0x00000000 +#define AR5K_PHY_PLL_RF5112 0x00000040 +#define AR5K_PHY_PLL_HALF_RATE 0x00000100 +#define AR5K_PHY_PLL_QUARTER_RATE 0x00000200 + +/* + * RF Buffer register + * + * It's obvious from the code that 0x989c is the buffer register but + * for the other special registers that we write to after sending each + * packet, i have no idea. So i'll name them BUFFER_CONTROL_X registers + * for now. It's interesting that they are also used for some other operations. + */ + +#define AR5K_RF_BUFFER 0x989c +#define AR5K_RF_BUFFER_CONTROL_0 0x98c0 /* Channel on 5110 */ +#define AR5K_RF_BUFFER_CONTROL_1 0x98c4 /* Bank 7 on 5112 */ +#define AR5K_RF_BUFFER_CONTROL_2 0x98cc /* Bank 7 on 5111 */ + +#define AR5K_RF_BUFFER_CONTROL_3 0x98d0 /* Bank 2 on 5112 */ + /* Channel set on 5111 */ + /* Used to read radio revision*/ + +#define AR5K_RF_BUFFER_CONTROL_4 0x98d4 /* RF Stage register on 5110 */ + /* Bank 0,1,2,6 on 5111 */ + /* Bank 1 on 5112 */ + /* Used during activation on 5111 */ + +#define AR5K_RF_BUFFER_CONTROL_5 0x98d8 /* Bank 3 on 5111 */ + /* Used during activation on 5111 */ + /* Channel on 5112 */ + /* Bank 6 on 5112 */ + +#define AR5K_RF_BUFFER_CONTROL_6 0x98dc /* Bank 3 on 5112 */ + +/* + * PHY RF stage register [5210] + */ +#define AR5K_PHY_RFSTG 0x98d4 +#define AR5K_PHY_RFSTG_DISABLE 0x00000021 + +/* + * BIN masks (?) + */ +#define AR5K_PHY_BIN_MASK_1 0x9900 +#define AR5K_PHY_BIN_MASK_2 0x9904 +#define AR5K_PHY_BIN_MASK_3 0x9908 + +#define AR5K_PHY_BIN_MASK_CTL 0x990c +#define AR5K_PHY_BIN_MASK_CTL_MASK_4 0x00003fff +#define AR5K_PHY_BIN_MASK_CTL_MASK_4_S 0 +#define AR5K_PHY_BIN_MASK_CTL_RATE 0xff000000 +#define AR5K_PHY_BIN_MASK_CTL_RATE_S 24 + +/* + * PHY Antenna control register + */ +#define AR5K_PHY_ANT_CTL 0x9910 /* Register Address */ +#define AR5K_PHY_ANT_CTL_TXRX_EN 0x00000001 /* Enable TX/RX (?) */ +#define AR5K_PHY_ANT_CTL_SECTORED_ANT 0x00000004 /* Sectored Antenna */ +#define AR5K_PHY_ANT_CTL_HITUNE5 0x00000008 /* Hitune5 (?) */ +#define AR5K_PHY_ANT_CTL_SWTABLE_IDLE 0x000003f0 /* Switch table idle (?) */ +#define AR5K_PHY_ANT_CTL_SWTABLE_IDLE_S 4 + +/* + * PHY receiver delay register [5111+] + */ +#define AR5K_PHY_RX_DELAY 0x9914 /* Register Address */ +#define AR5K_PHY_RX_DELAY_M 0x00003fff /* Mask for RX activate to receive delay (/100ns) */ + +/* + * PHY max rx length register (?) [5111] + */ +#define AR5K_PHY_MAX_RX_LEN 0x991c + +/* + * PHY timing register 4 + * I(nphase)/Q(adrature) calibration register [5111+] + */ +#define AR5K_PHY_IQ 0x9920 /* Register Address */ +#define AR5K_PHY_IQ_CORR_Q_Q_COFF 0x0000001f /* Mask for q correction info */ +#define AR5K_PHY_IQ_CORR_Q_I_COFF 0x000007e0 /* Mask for i correction info */ +#define AR5K_PHY_IQ_CORR_Q_I_COFF_S 5 +#define AR5K_PHY_IQ_CORR_ENABLE 0x00000800 /* Enable i/q correction */ +#define AR5K_PHY_IQ_CAL_NUM_LOG_MAX 0x0000f000 /* Mask for max number of samples in log scale */ +#define AR5K_PHY_IQ_CAL_NUM_LOG_MAX_S 12 +#define AR5K_PHY_IQ_RUN 0x00010000 /* Run i/q calibration */ +#define AR5K_PHY_IQ_USE_PT_DF 0x00020000 /* Use pilot track df (?) */ +#define AR5K_PHY_IQ_EARLY_TRIG_THR 0x00200000 /* Early trigger threshold (?) (field) */ +#define AR5K_PHY_IQ_PILOT_MASK_EN 0x10000000 /* Enable pilot mask (?) */ +#define AR5K_PHY_IQ_CHAN_MASK_EN 0x20000000 /* Enable channel mask (?) */ +#define AR5K_PHY_IQ_SPUR_FILT_EN 0x40000000 /* Enable spur filter */ +#define AR5K_PHY_IQ_SPUR_RSSI_EN 0x80000000 /* Enable spur rssi */ + +/* + * PHY timing register 5 + * OFDM Self-correlator Cyclic RSSI threshold params + * (Check out bb_cycpwr_thr1 on ANI patent) + */ +#define AR5K_PHY_OFDM_SELFCORR 0x9924 /* Register Address */ +#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1_EN 0x00000001 /* Enable cyclic RSSI thr 1 */ +#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1 0x000000fe /* Mask for Cyclic RSSI threshold 1 */ +#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1_S 1 +#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR3 0x00000100 /* Cyclic RSSI threshold 3 (field) (?) */ +#define AR5K_PHY_OFDM_SELFCORR_RSSI_1ATHR_EN 0x00008000 /* Enable 1A RSSI threshold (?) */ +#define AR5K_PHY_OFDM_SELFCORR_RSSI_1ATHR 0x00010000 /* 1A RSSI threshold (field) (?) */ +#define AR5K_PHY_OFDM_SELFCORR_LSCTHR_HIRSSI 0x00800000 /* Long sc threshold hi rssi (?) */ + +/* + * PHY-only warm reset register + */ +#define AR5K_PHY_WARM_RESET 0x9928 + +/* + * PHY-only control register + */ +#define AR5K_PHY_CTL 0x992c /* Register Address */ +#define AR5K_PHY_CTL_RX_DRAIN_RATE 0x00000001 /* RX drain rate (?) */ +#define AR5K_PHY_CTL_LATE_TX_SIG_SYM 0x00000002 /* Late tx signal symbol (?) */ +#define AR5K_PHY_CTL_GEN_SCRAMBLER 0x00000004 /* Generate scrambler */ +#define AR5K_PHY_CTL_TX_ANT_SEL 0x00000008 /* TX antenna select */ +#define AR5K_PHY_CTL_TX_ANT_STATIC 0x00000010 /* Static TX antenna */ +#define AR5K_PHY_CTL_RX_ANT_SEL 0x00000020 /* RX antenna select */ +#define AR5K_PHY_CTL_RX_ANT_STATIC 0x00000040 /* Static RX antenna */ +#define AR5K_PHY_CTL_LOW_FREQ_SLE_EN 0x00000080 /* Enable low freq sleep */ + +/* + * PHY PAPD probe register [5111+] + */ +#define AR5K_PHY_PAPD_PROBE 0x9930 +#define AR5K_PHY_PAPD_PROBE_SH_HI_PAR 0x00000001 +#define AR5K_PHY_PAPD_PROBE_PCDAC_BIAS 0x00000002 +#define AR5K_PHY_PAPD_PROBE_COMP_GAIN 0x00000040 +#define AR5K_PHY_PAPD_PROBE_TXPOWER 0x00007e00 +#define AR5K_PHY_PAPD_PROBE_TXPOWER_S 9 +#define AR5K_PHY_PAPD_PROBE_TX_NEXT 0x00008000 +#define AR5K_PHY_PAPD_PROBE_PREDIST_EN 0x00010000 +#define AR5K_PHY_PAPD_PROBE_TYPE 0x01800000 /* [5112+] */ +#define AR5K_PHY_PAPD_PROBE_TYPE_S 23 +#define AR5K_PHY_PAPD_PROBE_TYPE_OFDM 0 +#define AR5K_PHY_PAPD_PROBE_TYPE_XR 1 +#define AR5K_PHY_PAPD_PROBE_TYPE_CCK 2 +#define AR5K_PHY_PAPD_PROBE_GAINF 0xfe000000 +#define AR5K_PHY_PAPD_PROBE_GAINF_S 25 +#define AR5K_PHY_PAPD_PROBE_INI_5111 0x00004883 /* [5212+] */ +#define AR5K_PHY_PAPD_PROBE_INI_5112 0x00004882 /* [5212+] */ + +/* + * PHY TX rate power registers [5112+] + */ +#define AR5K_PHY_TXPOWER_RATE1 0x9934 +#define AR5K_PHY_TXPOWER_RATE2 0x9938 +#define AR5K_PHY_TXPOWER_RATE_MAX 0x993c +#define AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE 0x00000040 +#define AR5K_PHY_TXPOWER_RATE3 0xa234 +#define AR5K_PHY_TXPOWER_RATE4 0xa238 + +/* + * PHY frame control register [5111+] + */ +#define AR5K_PHY_FRAME_CTL_5210 0x9804 +#define AR5K_PHY_FRAME_CTL_5211 0x9944 +#define AR5K_PHY_FRAME_CTL (ah->ah_version == AR5K_AR5210 ? \ + AR5K_PHY_FRAME_CTL_5210 : AR5K_PHY_FRAME_CTL_5211) +/*---[5111+]---*/ +#define AR5K_PHY_FRAME_CTL_TX_CLIP 0x00000038 /* Mask for tx clip (?) */ +#define AR5K_PHY_FRAME_CTL_TX_CLIP_S 3 +#define AR5K_PHY_FRAME_CTL_PREP_CHINFO 0x00010000 /* Prepend chan info */ +#define AR5K_PHY_FRAME_CTL_EMU 0x80000000 +#define AR5K_PHY_FRAME_CTL_EMU_S 31 +/*---[5110/5111]---*/ +#define AR5K_PHY_FRAME_CTL_TIMING_ERR 0x01000000 /* PHY timing error */ +#define AR5K_PHY_FRAME_CTL_PARITY_ERR 0x02000000 /* Parity error */ +#define AR5K_PHY_FRAME_CTL_ILLRATE_ERR 0x04000000 /* Illegal rate */ +#define AR5K_PHY_FRAME_CTL_ILLLEN_ERR 0x08000000 /* Illegal length */ +#define AR5K_PHY_FRAME_CTL_SERVICE_ERR 0x20000000 +#define AR5K_PHY_FRAME_CTL_TXURN_ERR 0x40000000 /* TX underrun */ +#define AR5K_PHY_FRAME_CTL_INI AR5K_PHY_FRAME_CTL_SERVICE_ERR | \ + AR5K_PHY_FRAME_CTL_TXURN_ERR | \ + AR5K_PHY_FRAME_CTL_ILLLEN_ERR | \ + AR5K_PHY_FRAME_CTL_ILLRATE_ERR | \ + AR5K_PHY_FRAME_CTL_PARITY_ERR | \ + AR5K_PHY_FRAME_CTL_TIMING_ERR + +/* + * PHY Tx Power adjustment register [5212A+] + */ +#define AR5K_PHY_TX_PWR_ADJ 0x994c +#define AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA 0x00000fc0 +#define AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA_S 6 +#define AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX 0x00fc0000 +#define AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX_S 18 + +/* + * PHY radar detection register [5111+] + */ +#define AR5K_PHY_RADAR 0x9954 +#define AR5K_PHY_RADAR_ENABLE 0x00000001 +#define AR5K_PHY_RADAR_DISABLE 0x00000000 +#define AR5K_PHY_RADAR_INBANDTHR 0x0000003e /* Inband threshold + 5-bits, units unknown {0..31} + (? MHz ?) */ +#define AR5K_PHY_RADAR_INBANDTHR_S 1 + +#define AR5K_PHY_RADAR_PRSSI_THR 0x00000fc0 /* Pulse RSSI/SNR threshold + 6-bits, dBm range {0..63} + in dBm units. */ +#define AR5K_PHY_RADAR_PRSSI_THR_S 6 + +#define AR5K_PHY_RADAR_PHEIGHT_THR 0x0003f000 /* Pulse height threshold + 6-bits, dBm range {0..63} + in dBm units. */ +#define AR5K_PHY_RADAR_PHEIGHT_THR_S 12 + +#define AR5K_PHY_RADAR_RSSI_THR 0x00fc0000 /* Radar RSSI/SNR threshold. + 6-bits, dBm range {0..63} + in dBm units. */ +#define AR5K_PHY_RADAR_RSSI_THR_S 18 + +#define AR5K_PHY_RADAR_FIRPWR_THR 0x7f000000 /* Finite Impulse Response + filter power out threshold. + 7-bits, standard power range + {0..127} in 1/2 dBm units. */ +#define AR5K_PHY_RADAR_FIRPWR_THRS 24 + +/* + * PHY antenna switch table registers + */ +#define AR5K_PHY_ANT_SWITCH_TABLE_0 0x9960 +#define AR5K_PHY_ANT_SWITCH_TABLE_1 0x9964 + +/* + * PHY Noise floor threshold + */ +#define AR5K_PHY_NFTHRES 0x9968 + +/* + * Sigma Delta register (?) [5213] + */ +#define AR5K_PHY_SIGMA_DELTA 0x996C +#define AR5K_PHY_SIGMA_DELTA_ADC_SEL 0x00000003 +#define AR5K_PHY_SIGMA_DELTA_ADC_SEL_S 0 +#define AR5K_PHY_SIGMA_DELTA_FILT2 0x000000f8 +#define AR5K_PHY_SIGMA_DELTA_FILT2_S 3 +#define AR5K_PHY_SIGMA_DELTA_FILT1 0x00001f00 +#define AR5K_PHY_SIGMA_DELTA_FILT1_S 8 +#define AR5K_PHY_SIGMA_DELTA_ADC_CLIP 0x01ffe000 +#define AR5K_PHY_SIGMA_DELTA_ADC_CLIP_S 13 + +/* + * RF restart register [5112+] (?) + */ +#define AR5K_PHY_RESTART 0x9970 /* restart */ +#define AR5K_PHY_RESTART_DIV_GC 0x001c0000 /* Fast diversity gc_limit (?) */ +#define AR5K_PHY_RESTART_DIV_GC_S 18 + +/* + * RF Bus access request register (for synth-oly channel switching) + */ +#define AR5K_PHY_RFBUS_REQ 0x997C +#define AR5K_PHY_RFBUS_REQ_REQUEST 0x00000001 + +/* + * Spur mitigation masks (?) + */ +#define AR5K_PHY_TIMING_7 0x9980 +#define AR5K_PHY_TIMING_8 0x9984 +#define AR5K_PHY_TIMING_8_PILOT_MASK_2 0x000fffff +#define AR5K_PHY_TIMING_8_PILOT_MASK_2_S 0 + +#define AR5K_PHY_BIN_MASK2_1 0x9988 +#define AR5K_PHY_BIN_MASK2_2 0x998c +#define AR5K_PHY_BIN_MASK2_3 0x9990 + +#define AR5K_PHY_BIN_MASK2_4 0x9994 +#define AR5K_PHY_BIN_MASK2_4_MASK_4 0x00003fff +#define AR5K_PHY_BIN_MASK2_4_MASK_4_S 0 + +#define AR5K_PHY_TIMING_9 0x9998 +#define AR5K_PHY_TIMING_10 0x999c +#define AR5K_PHY_TIMING_10_PILOT_MASK_2 0x000fffff +#define AR5K_PHY_TIMING_10_PILOT_MASK_2_S 0 + +/* + * Spur mitigation control + */ +#define AR5K_PHY_TIMING_11 0x99a0 /* Register address */ +#define AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE 0x000fffff /* Spur delta phase */ +#define AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE_S 0 +#define AR5K_PHY_TIMING_11_SPUR_FREQ_SD 0x3ff00000 /* Freq sigma delta */ +#define AR5K_PHY_TIMING_11_SPUR_FREQ_SD_S 20 +#define AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC 0x40000000 /* Spur filter in AGC detector */ +#define AR5K_PHY_TIMING_11_USE_SPUR_IN_SELFCOR 0x80000000 /* Spur filter in OFDM self correlator */ + +/* + * Gain tables + */ +#define AR5K_BB_GAIN_BASE 0x9b00 /* BaseBand Amplifier Gain table base address */ +#define AR5K_BB_GAIN(_n) (AR5K_BB_GAIN_BASE + ((_n) << 2)) +#define AR5K_RF_GAIN_BASE 0x9a00 /* RF Amplrifier Gain table base address */ +#define AR5K_RF_GAIN(_n) (AR5K_RF_GAIN_BASE + ((_n) << 2)) + +/* + * PHY timing IQ calibration result register [5111+] + */ +#define AR5K_PHY_IQRES_CAL_PWR_I 0x9c10 /* I (Inphase) power value */ +#define AR5K_PHY_IQRES_CAL_PWR_Q 0x9c14 /* Q (Quadrature) power value */ +#define AR5K_PHY_IQRES_CAL_CORR 0x9c18 /* I/Q Correlation */ + +/* + * PHY current RSSI register [5111+] + */ +#define AR5K_PHY_CURRENT_RSSI 0x9c1c + +/* + * PHY RF Bus grant register + */ +#define AR5K_PHY_RFBUS_GRANT 0x9c20 +#define AR5K_PHY_RFBUS_GRANT_OK 0x00000001 + +/* + * PHY ADC test register + */ +#define AR5K_PHY_ADC_TEST 0x9c24 +#define AR5K_PHY_ADC_TEST_I 0x00000001 +#define AR5K_PHY_ADC_TEST_Q 0x00000200 + +/* + * PHY DAC test register + */ +#define AR5K_PHY_DAC_TEST 0x9c28 +#define AR5K_PHY_DAC_TEST_I 0x00000001 +#define AR5K_PHY_DAC_TEST_Q 0x00000200 + +/* + * PHY PTAT register (?) + */ +#define AR5K_PHY_PTAT 0x9c2c + +/* + * PHY Illegal TX rate register [5112+] + */ +#define AR5K_PHY_BAD_TX_RATE 0x9c30 + +/* + * PHY SPUR Power register [5112+] + */ +#define AR5K_PHY_SPUR_PWR 0x9c34 /* Register Address */ +#define AR5K_PHY_SPUR_PWR_I 0x00000001 /* SPUR Power estimate for I (field) */ +#define AR5K_PHY_SPUR_PWR_Q 0x00000100 /* SPUR Power estimate for Q (field) */ +#define AR5K_PHY_SPUR_PWR_FILT 0x00010000 /* Power with SPUR removed (field) */ + +/* + * PHY Channel status register [5112+] (?) + */ +#define AR5K_PHY_CHAN_STATUS 0x9c38 +#define AR5K_PHY_CHAN_STATUS_BT_ACT 0x00000001 +#define AR5K_PHY_CHAN_STATUS_RX_CLR_RAW 0x00000002 +#define AR5K_PHY_CHAN_STATUS_RX_CLR_MAC 0x00000004 +#define AR5K_PHY_CHAN_STATUS_RX_CLR_PAP 0x00000008 + +/* + * Heavy clip enable register + */ +#define AR5K_PHY_HEAVY_CLIP_ENABLE 0x99e0 + +/* + * PHY clock sleep registers [5112+] + */ +#define AR5K_PHY_SCLOCK 0x99f0 +#define AR5K_PHY_SCLOCK_32MHZ 0x0000000c +#define AR5K_PHY_SDELAY 0x99f4 +#define AR5K_PHY_SDELAY_32MHZ 0x000000ff +#define AR5K_PHY_SPENDING 0x99f8 + + +/* + * PHY PAPD I (power?) table (?) + * (92! entries) + */ +#define AR5K_PHY_PAPD_I_BASE 0xa000 +#define AR5K_PHY_PAPD_I(_n) (AR5K_PHY_PAPD_I_BASE + ((_n) << 2)) + +/* + * PHY PCDAC TX power table + */ +#define AR5K_PHY_PCDAC_TXPOWER_BASE 0xa180 +#define AR5K_PHY_PCDAC_TXPOWER(_n) (AR5K_PHY_PCDAC_TXPOWER_BASE + ((_n) << 2)) + +/* + * PHY mode register [5111+] + */ +#define AR5K_PHY_MODE 0x0a200 /* Register Address */ +#define AR5K_PHY_MODE_MOD 0x00000001 /* PHY Modulation bit */ +#define AR5K_PHY_MODE_MOD_OFDM 0 +#define AR5K_PHY_MODE_MOD_CCK 1 +#define AR5K_PHY_MODE_FREQ 0x00000002 /* Freq mode bit */ +#define AR5K_PHY_MODE_FREQ_5GHZ 0 +#define AR5K_PHY_MODE_FREQ_2GHZ 2 +#define AR5K_PHY_MODE_MOD_DYN 0x00000004 /* Enable Dynamic OFDM/CCK mode [5112+] */ +#define AR5K_PHY_MODE_RAD 0x00000008 /* [5212+] */ +#define AR5K_PHY_MODE_RAD_RF5111 0 +#define AR5K_PHY_MODE_RAD_RF5112 8 +#define AR5K_PHY_MODE_XR 0x00000010 /* Enable XR mode [5112+] */ +#define AR5K_PHY_MODE_HALF_RATE 0x00000020 /* Enable Half rate (test) */ +#define AR5K_PHY_MODE_QUARTER_RATE 0x00000040 /* Enable Quarter rat (test) */ + +/* + * PHY CCK transmit control register [5111+ (?)] + */ +#define AR5K_PHY_CCKTXCTL 0xa204 +#define AR5K_PHY_CCKTXCTL_WORLD 0x00000000 +#define AR5K_PHY_CCKTXCTL_JAPAN 0x00000010 +#define AR5K_PHY_CCKTXCTL_SCRAMBLER_DIS 0x00000001 +#define AR5K_PHY_CCKTXCTK_DAC_SCALE 0x00000004 + +/* + * PHY CCK Cross-correlator Barker RSSI threshold register [5212+] + */ +#define AR5K_PHY_CCK_CROSSCORR 0xa208 +#define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR 0x0000003f +#define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR_S 0 + +/* Same address is used for antenna diversity activation */ +#define AR5K_PHY_FAST_ANT_DIV 0xa208 +#define AR5K_PHY_FAST_ANT_DIV_EN 0x00002000 + +/* + * PHY 2GHz gain register [5111+] + */ +#define AR5K_PHY_GAIN_2GHZ 0xa20c +#define AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX 0x00fc0000 +#define AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX_S 18 +#define AR5K_PHY_GAIN_2GHZ_INI_5111 0x6480416c + +#define AR5K_PHY_CCK_RX_CTL_4 0xa21c +#define AR5K_PHY_CCK_RX_CTL_4_FREQ_EST_SHORT 0x01f80000 +#define AR5K_PHY_CCK_RX_CTL_4_FREQ_EST_SHORT_S 19 + +#define AR5K_PHY_DAG_CCK_CTL 0xa228 +#define AR5K_PHY_DAG_CCK_CTL_EN_RSSI_THR 0x00000200 +#define AR5K_PHY_DAG_CCK_CTL_RSSI_THR 0x0001fc00 +#define AR5K_PHY_DAG_CCK_CTL_RSSI_THR_S 10 + +#define AR5K_PHY_FAST_ADC 0xa24c + +#define AR5K_PHY_BLUETOOTH 0xa254 + +/* + * Transmit Power Control register + * [2413+] + */ +#define AR5K_PHY_TPC_RG1 0xa258 +#define AR5K_PHY_TPC_RG1_NUM_PD_GAIN 0x0000c000 +#define AR5K_PHY_TPC_RG1_NUM_PD_GAIN_S 14 +#define AR5K_PHY_TPC_RG1_PDGAIN_1 0x00030000 +#define AR5K_PHY_TPC_RG1_PDGAIN_1_S 16 +#define AR5K_PHY_TPC_RG1_PDGAIN_2 0x000c0000 +#define AR5K_PHY_TPC_RG1_PDGAIN_2_S 18 +#define AR5K_PHY_TPC_RG1_PDGAIN_3 0x00300000 +#define AR5K_PHY_TPC_RG1_PDGAIN_3_S 20 + +#define AR5K_PHY_TPC_RG5 0xa26C +#define AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP 0x0000000F +#define AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP_S 0 +#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1 0x000003F0 +#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1_S 4 +#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2 0x0000FC00 +#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2_S 10 +#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3 0x003F0000 +#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3_S 16 +#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4 0x0FC00000 +#define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4_S 22 + +/* + * PHY PDADC Tx power table + */ +#define AR5K_PHY_PDADC_TXPOWER_BASE 0xa280 +#define AR5K_PHY_PDADC_TXPOWER(_n) (AR5K_PHY_PDADC_TXPOWER_BASE + ((_n) << 2)) diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c new file mode 100644 index 00000000000..bd0a97a38d3 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -0,0 +1,1323 @@ +/* + * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2007-2008 Luis Rodriguez <mcgrof@winlab.rutgers.edu> + * Copyright (c) 2007-2008 Pavel Roskin <proski@gnu.org> + * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +#define _ATH5K_RESET + +/*****************************\ + Reset functions and helpers +\*****************************/ + +#include <linux/pci.h> /* To determine if a card is pci-e */ +#include <linux/log2.h> +#include "ath5k.h" +#include "reg.h" +#include "base.h" +#include "debug.h" + +/** + * ath5k_hw_write_ofdm_timings - set OFDM timings on AR5212 + * + * @ah: the &struct ath5k_hw + * @channel: the currently set channel upon reset + * + * Write the delta slope coefficient (used on pilot tracking ?) for OFDM + * operation on the AR5212 upon reset. This is a helper for ath5k_hw_reset(). + * + * Since delta slope is floating point we split it on its exponent and + * mantissa and provide these values on hw. + * + * For more infos i think this patent is related + * http://www.freepatentsonline.com/7184495.html + */ +static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + /* Get exponent and mantissa and set it */ + u32 coef_scaled, coef_exp, coef_man, + ds_coef_exp, ds_coef_man, clock; + + BUG_ON(!(ah->ah_version == AR5K_AR5212) || + !(channel->hw_value & CHANNEL_OFDM)); + + /* Get coefficient + * ALGO: coef = (5 * clock * carrier_freq) / 2) + * we scale coef by shifting clock value by 24 for + * better precision since we use integers */ + /* TODO: Half/quarter rate */ + clock = ath5k_hw_htoclock(1, channel->hw_value & CHANNEL_TURBO); + + coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq; + + /* Get exponent + * ALGO: coef_exp = 14 - highest set bit position */ + coef_exp = ilog2(coef_scaled); + + /* Doesn't make sense if it's zero*/ + if (!coef_scaled || !coef_exp) + return -EINVAL; + + /* Note: we've shifted coef_scaled by 24 */ + coef_exp = 14 - (coef_exp - 24); + + + /* Get mantissa (significant digits) + * ALGO: coef_mant = floor(coef_scaled* 2^coef_exp+0.5) */ + coef_man = coef_scaled + + (1 << (24 - coef_exp - 1)); + + /* Calculate delta slope coefficient exponent + * and mantissa (remove scaling) and set them on hw */ + ds_coef_man = coef_man >> (24 - coef_exp); + ds_coef_exp = coef_exp - 16; + + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3, + AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man); + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3, + AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp); + + return 0; +} + + +/* + * index into rates for control rates, we can set it up like this because + * this is only used for AR5212 and we know it supports G mode + */ +static const unsigned int control_rates[] = + { 0, 1, 1, 1, 4, 4, 6, 6, 8, 8, 8, 8 }; + +/** + * ath5k_hw_write_rate_duration - fill rate code to duration table + * + * @ah: the &struct ath5k_hw + * @mode: one of enum ath5k_driver_mode + * + * Write the rate code to duration table upon hw reset. This is a helper for + * ath5k_hw_reset(). It seems all this is doing is setting an ACK timeout on + * the hardware, based on current mode, for each rate. The rates which are + * capable of short preamble (802.11b rates 2Mbps, 5.5Mbps, and 11Mbps) have + * different rate code so we write their value twice (one for long preample + * and one for short). + * + * Note: Band doesn't matter here, if we set the values for OFDM it works + * on both a and g modes. So all we have to do is set values for all g rates + * that include all OFDM and CCK rates. If we operate in turbo or xr/half/ + * quarter rate mode, we need to use another set of bitrates (that's why we + * need the mode parameter) but we don't handle these proprietary modes yet. + */ +static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah, + unsigned int mode) +{ + struct ath5k_softc *sc = ah->ah_sc; + struct ieee80211_rate *rate; + unsigned int i; + + /* Write rate duration table */ + for (i = 0; i < sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates; i++) { + u32 reg; + u16 tx_time; + + rate = &sc->sbands[IEEE80211_BAND_2GHZ].bitrates[control_rates[i]]; + + /* Set ACK timeout */ + reg = AR5K_RATE_DUR(rate->hw_value); + + /* An ACK frame consists of 10 bytes. If you add the FCS, + * which ieee80211_generic_frame_duration() adds, + * its 14 bytes. Note we use the control rate and not the + * actual rate for this rate. See mac80211 tx.c + * ieee80211_duration() for a brief description of + * what rate we should choose to TX ACKs. */ + tx_time = le16_to_cpu(ieee80211_generic_frame_duration(sc->hw, + sc->vif, 10, rate)); + + ath5k_hw_reg_write(ah, tx_time, reg); + + if (!(rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)) + continue; + + /* + * We're not distinguishing short preamble here, + * This is true, all we'll get is a longer value here + * which is not necessarilly bad. We could use + * export ieee80211_frame_duration() but that needs to be + * fixed first to be properly used by mac802111 drivers: + * + * - remove erp stuff and let the routine figure ofdm + * erp rates + * - remove passing argument ieee80211_local as + * drivers don't have access to it + * - move drivers using ieee80211_generic_frame_duration() + * to this + */ + ath5k_hw_reg_write(ah, tx_time, + reg + (AR5K_SET_SHORT_PREAMBLE << 2)); + } +} + +/* + * Reset chipset + */ +static int ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val) +{ + int ret; + u32 mask = val ? val : ~0U; + + ATH5K_TRACE(ah->ah_sc); + + /* Read-and-clear RX Descriptor Pointer*/ + ath5k_hw_reg_read(ah, AR5K_RXDP); + + /* + * Reset the device and wait until success + */ + ath5k_hw_reg_write(ah, val, AR5K_RESET_CTL); + + /* Wait at least 128 PCI clocks */ + udelay(15); + + if (ah->ah_version == AR5K_AR5210) { + val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA + | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY; + mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA + | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY; + } else { + val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND; + mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND; + } + + ret = ath5k_hw_register_timeout(ah, AR5K_RESET_CTL, mask, val, false); + + /* + * Reset configuration register (for hw byte-swap). Note that this + * is only set for big endian. We do the necessary magic in + * AR5K_INIT_CFG. + */ + if ((val & AR5K_RESET_CTL_PCU) == 0) + ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG); + + return ret; +} + +/* + * Sleep control + */ +int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, + bool set_chip, u16 sleep_duration) +{ + unsigned int i; + u32 staid, data; + + ATH5K_TRACE(ah->ah_sc); + staid = ath5k_hw_reg_read(ah, AR5K_STA_ID1); + + switch (mode) { + case AR5K_PM_AUTO: + staid &= ~AR5K_STA_ID1_DEFAULT_ANTENNA; + /* fallthrough */ + case AR5K_PM_NETWORK_SLEEP: + if (set_chip) + ath5k_hw_reg_write(ah, + AR5K_SLEEP_CTL_SLE_ALLOW | + sleep_duration, + AR5K_SLEEP_CTL); + + staid |= AR5K_STA_ID1_PWR_SV; + break; + + case AR5K_PM_FULL_SLEEP: + if (set_chip) + ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_SLP, + AR5K_SLEEP_CTL); + + staid |= AR5K_STA_ID1_PWR_SV; + break; + + case AR5K_PM_AWAKE: + + staid &= ~AR5K_STA_ID1_PWR_SV; + + if (!set_chip) + goto commit; + + /* Preserve sleep duration */ + data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL); + if (data & 0xffc00000) + data = 0; + else + data = data & 0xfffcffff; + + ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); + udelay(15); + + for (i = 50; i > 0; i--) { + /* Check if the chip did wake up */ + if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) & + AR5K_PCICFG_SPWR_DN) == 0) + break; + + /* Wait a bit and retry */ + udelay(200); + ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); + } + + /* Fail if the chip didn't wake up */ + if (i <= 0) + return -EIO; + + break; + + default: + return -EINVAL; + } + +commit: + ah->ah_power_mode = mode; + ath5k_hw_reg_write(ah, staid, AR5K_STA_ID1); + + return 0; +} + +/* + * Bring up MAC + PHY Chips and program PLL + * TODO: Half/Quarter rate support + */ +int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) +{ + struct pci_dev *pdev = ah->ah_sc->pdev; + u32 turbo, mode, clock, bus_flags; + int ret; + + turbo = 0; + mode = 0; + clock = 0; + + ATH5K_TRACE(ah->ah_sc); + + /* Wakeup the device */ + ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); + if (ret) { + ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n"); + return ret; + } + + if (ah->ah_version != AR5K_AR5210) { + /* + * Get channel mode flags + */ + + if (ah->ah_radio >= AR5K_RF5112) { + mode = AR5K_PHY_MODE_RAD_RF5112; + clock = AR5K_PHY_PLL_RF5112; + } else { + mode = AR5K_PHY_MODE_RAD_RF5111; /*Zero*/ + clock = AR5K_PHY_PLL_RF5111; /*Zero*/ + } + + if (flags & CHANNEL_2GHZ) { + mode |= AR5K_PHY_MODE_FREQ_2GHZ; + clock |= AR5K_PHY_PLL_44MHZ; + + if (flags & CHANNEL_CCK) { + mode |= AR5K_PHY_MODE_MOD_CCK; + } else if (flags & CHANNEL_OFDM) { + /* XXX Dynamic OFDM/CCK is not supported by the + * AR5211 so we set MOD_OFDM for plain g (no + * CCK headers) operation. We need to test + * this, 5211 might support ofdm-only g after + * all, there are also initial register values + * in the code for g mode (see initvals.c). */ + if (ah->ah_version == AR5K_AR5211) + mode |= AR5K_PHY_MODE_MOD_OFDM; + else + mode |= AR5K_PHY_MODE_MOD_DYN; + } else { + ATH5K_ERR(ah->ah_sc, + "invalid radio modulation mode\n"); + return -EINVAL; + } + } else if (flags & CHANNEL_5GHZ) { + mode |= AR5K_PHY_MODE_FREQ_5GHZ; + + if (ah->ah_radio == AR5K_RF5413) + clock = AR5K_PHY_PLL_40MHZ_5413; + else + clock |= AR5K_PHY_PLL_40MHZ; + + if (flags & CHANNEL_OFDM) + mode |= AR5K_PHY_MODE_MOD_OFDM; + else { + ATH5K_ERR(ah->ah_sc, + "invalid radio modulation mode\n"); + return -EINVAL; + } + } else { + ATH5K_ERR(ah->ah_sc, "invalid radio frequency mode\n"); + return -EINVAL; + } + + if (flags & CHANNEL_TURBO) + turbo = AR5K_PHY_TURBO_MODE | AR5K_PHY_TURBO_SHORT; + } else { /* Reset the device */ + + /* ...enable Atheros turbo mode if requested */ + if (flags & CHANNEL_TURBO) + ath5k_hw_reg_write(ah, AR5K_PHY_TURBO_MODE, + AR5K_PHY_TURBO); + } + + /* reseting PCI on PCI-E cards results card to hang + * and always return 0xffff... so we ingore that flag + * for PCI-E cards */ + bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI; + + /* Reset chipset */ + if (ah->ah_version == AR5K_AR5210) { + ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | + AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | + AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); + mdelay(2); + } else { + ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | + AR5K_RESET_CTL_BASEBAND | bus_flags); + } + if (ret) { + ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip\n"); + return -EIO; + } + + /* ...wakeup again!*/ + ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); + if (ret) { + ATH5K_ERR(ah->ah_sc, "failed to resume the MAC Chip\n"); + return ret; + } + + /* ...final warm reset */ + if (ath5k_hw_nic_reset(ah, 0)) { + ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n"); + return -EIO; + } + + if (ah->ah_version != AR5K_AR5210) { + + /* ...update PLL if needed */ + if (ath5k_hw_reg_read(ah, AR5K_PHY_PLL) != clock) { + ath5k_hw_reg_write(ah, clock, AR5K_PHY_PLL); + udelay(300); + } + + /* ...set the PHY operating mode */ + ath5k_hw_reg_write(ah, mode, AR5K_PHY_MODE); + ath5k_hw_reg_write(ah, turbo, AR5K_PHY_TURBO); + } + + return 0; +} + +/* + * If there is an external 32KHz crystal available, use it + * as ref. clock instead of 32/40MHz clock and baseband clocks + * to save power during sleep or restore normal 32/40MHz + * operation. + * + * XXX: When operating on 32KHz certain PHY registers (27 - 31, + * 123 - 127) require delay on access. + */ +static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u32 scal, spending, usec32; + + /* Only set 32KHz settings if we have an external + * 32KHz crystal present */ + if ((AR5K_EEPROM_HAS32KHZCRYSTAL(ee->ee_misc1) || + AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(ee->ee_misc1)) && + enable) { + + /* 1 usec/cycle */ + AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, 1); + /* Set up tsf increment on each cycle */ + AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 61); + + /* Set baseband sleep control registers + * and sleep control rate */ + ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR); + + if ((ah->ah_radio == AR5K_RF5112) || + (ah->ah_radio == AR5K_RF5413) || + (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) + spending = 0x14; + else + spending = 0x18; + ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING); + + if ((ah->ah_radio == AR5K_RF5112) || + (ah->ah_radio == AR5K_RF5413) || + (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) { + ath5k_hw_reg_write(ah, 0x26, AR5K_PHY_SLMT); + ath5k_hw_reg_write(ah, 0x0d, AR5K_PHY_SCAL); + ath5k_hw_reg_write(ah, 0x07, AR5K_PHY_SCLOCK); + ath5k_hw_reg_write(ah, 0x3f, AR5K_PHY_SDELAY); + AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG, + AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x02); + } else { + ath5k_hw_reg_write(ah, 0x0a, AR5K_PHY_SLMT); + ath5k_hw_reg_write(ah, 0x0c, AR5K_PHY_SCAL); + ath5k_hw_reg_write(ah, 0x03, AR5K_PHY_SCLOCK); + ath5k_hw_reg_write(ah, 0x20, AR5K_PHY_SDELAY); + AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG, + AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x03); + } + + /* Enable sleep clock operation */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, + AR5K_PCICFG_SLEEP_CLOCK_EN); + + } else { + + /* Disable sleep clock operation and + * restore default parameters */ + AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, + AR5K_PCICFG_SLEEP_CLOCK_EN); + + AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG, + AR5K_PCICFG_SLEEP_CLOCK_RATE, 0); + + ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR); + ath5k_hw_reg_write(ah, AR5K_PHY_SLMT_32MHZ, AR5K_PHY_SLMT); + + if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)) + scal = AR5K_PHY_SCAL_32MHZ_2417; + else if (ee->ee_is_hb63) + scal = AR5K_PHY_SCAL_32MHZ_HB63; + else + scal = AR5K_PHY_SCAL_32MHZ; + ath5k_hw_reg_write(ah, scal, AR5K_PHY_SCAL); + + ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK); + ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY); + + if ((ah->ah_radio == AR5K_RF5112) || + (ah->ah_radio == AR5K_RF5413) || + (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) + spending = 0x14; + else + spending = 0x18; + ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING); + + if ((ah->ah_radio == AR5K_RF5112) || + (ah->ah_radio == AR5K_RF5413)) + usec32 = 39; + else + usec32 = 31; + AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, usec32); + + AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 1); + } + return; +} + +/* TODO: Half/Quarter rate */ +static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + if (ah->ah_version == AR5K_AR5212 && + ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) { + + /* Setup ADC control */ + ath5k_hw_reg_write(ah, + (AR5K_REG_SM(2, + AR5K_PHY_ADC_CTL_INBUFGAIN_OFF) | + AR5K_REG_SM(2, + AR5K_PHY_ADC_CTL_INBUFGAIN_ON) | + AR5K_PHY_ADC_CTL_PWD_DAC_OFF | + AR5K_PHY_ADC_CTL_PWD_ADC_OFF), + AR5K_PHY_ADC_CTL); + + + + /* Disable barker RSSI threshold */ + AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_DAG_CCK_CTL, + AR5K_PHY_DAG_CCK_CTL_EN_RSSI_THR); + + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DAG_CCK_CTL, + AR5K_PHY_DAG_CCK_CTL_RSSI_THR, 2); + + /* Set the mute mask */ + ath5k_hw_reg_write(ah, 0x0000000f, AR5K_SEQ_MASK); + } + + /* Clear PHY_BLUETOOTH to allow RX_CLEAR line debug */ + if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212B) + ath5k_hw_reg_write(ah, 0, AR5K_PHY_BLUETOOTH); + + /* Enable DCU double buffering */ + if (ah->ah_phy_revision > AR5K_SREV_PHY_5212B) + AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG, + AR5K_TXCFG_DCU_DBL_BUF_DIS); + + /* Set DAC/ADC delays */ + if (ah->ah_version == AR5K_AR5212) { + u32 scal; + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)) + scal = AR5K_PHY_SCAL_32MHZ_2417; + else if (ee->ee_is_hb63) + scal = AR5K_PHY_SCAL_32MHZ_HB63; + else + scal = AR5K_PHY_SCAL_32MHZ; + ath5k_hw_reg_write(ah, scal, AR5K_PHY_SCAL); + } + + /* Set fast ADC */ + if ((ah->ah_radio == AR5K_RF5413) || + (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) { + u32 fast_adc = true; + + if (channel->center_freq == 2462 || + channel->center_freq == 2467) + fast_adc = 0; + + /* Only update if needed */ + if (ath5k_hw_reg_read(ah, AR5K_PHY_FAST_ADC) != fast_adc) + ath5k_hw_reg_write(ah, fast_adc, + AR5K_PHY_FAST_ADC); + } + + /* Fix for first revision of the RF5112 RF chipset */ + if (ah->ah_radio == AR5K_RF5112 && + ah->ah_radio_5ghz_revision < + AR5K_SREV_RAD_5112A) { + u32 data; + ath5k_hw_reg_write(ah, AR5K_PHY_CCKTXCTL_WORLD, + AR5K_PHY_CCKTXCTL); + if (channel->hw_value & CHANNEL_5GHZ) + data = 0xffb81020; + else + data = 0xffb80d20; + ath5k_hw_reg_write(ah, data, AR5K_PHY_FRAME_CTL); + } + + if (ah->ah_mac_srev < AR5K_SREV_AR5211) { + u32 usec_reg; + /* 5311 has different tx/rx latency masks + * from 5211, since we deal 5311 the same + * as 5211 when setting initvals, shift + * values here to their proper locations */ + usec_reg = ath5k_hw_reg_read(ah, AR5K_USEC_5211); + ath5k_hw_reg_write(ah, usec_reg & (AR5K_USEC_1 | + AR5K_USEC_32 | + AR5K_USEC_TX_LATENCY_5211 | + AR5K_REG_SM(29, + AR5K_USEC_RX_LATENCY_5210)), + AR5K_USEC_5211); + /* Clear QCU/DCU clock gating register */ + ath5k_hw_reg_write(ah, 0, AR5K_QCUDCU_CLKGT); + /* Set DAC/ADC delays */ + ath5k_hw_reg_write(ah, 0x08, AR5K_PHY_SCAL); + /* Enable PCU FIFO corruption ECO */ + AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211, + AR5K_DIAG_SW_ECO_ENABLE); + } +} + +static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah, + struct ieee80211_channel *channel, u8 *ant, u8 ee_mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + s16 cck_ofdm_pwr_delta; + + /* Adjust power delta for channel 14 */ + if (channel->center_freq == 2484) + cck_ofdm_pwr_delta = + ((ee->ee_cck_ofdm_power_delta - + ee->ee_scaled_cck_delta) * 2) / 10; + else + cck_ofdm_pwr_delta = + (ee->ee_cck_ofdm_power_delta * 2) / 10; + + /* Set CCK to OFDM power delta on tx power + * adjustment register */ + if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) { + if (channel->hw_value == CHANNEL_G) + ath5k_hw_reg_write(ah, + AR5K_REG_SM((ee->ee_cck_ofdm_gain_delta * -1), + AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA) | + AR5K_REG_SM((cck_ofdm_pwr_delta * -1), + AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX), + AR5K_PHY_TX_PWR_ADJ); + else + ath5k_hw_reg_write(ah, 0, AR5K_PHY_TX_PWR_ADJ); + } else { + /* For older revs we scale power on sw during tx power + * setup */ + ah->ah_txpower.txp_cck_ofdm_pwr_delta = cck_ofdm_pwr_delta; + ah->ah_txpower.txp_cck_ofdm_gainf_delta = + ee->ee_cck_ofdm_gain_delta; + } + + /* Set antenna idle switch table */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_ANT_CTL, + AR5K_PHY_ANT_CTL_SWTABLE_IDLE, + (ah->ah_ant_ctl[ee_mode][0] | + AR5K_PHY_ANT_CTL_TXRX_EN)); + + /* Set antenna switch tables */ + ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant[0]], + AR5K_PHY_ANT_SWITCH_TABLE_0); + ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant[1]], + AR5K_PHY_ANT_SWITCH_TABLE_1); + + /* Noise floor threshold */ + ath5k_hw_reg_write(ah, + AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]), + AR5K_PHY_NFTHRES); + + if ((channel->hw_value & CHANNEL_TURBO) && + (ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0)) { + /* Switch settling time (Turbo) */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING, + AR5K_PHY_SETTLING_SWITCH, + ee->ee_switch_settling_turbo[ee_mode]); + + /* Tx/Rx attenuation (Turbo) */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN, + AR5K_PHY_GAIN_TXRX_ATTEN, + ee->ee_atn_tx_rx_turbo[ee_mode]); + + /* ADC/PGA desired size (Turbo) */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, + AR5K_PHY_DESIRED_SIZE_ADC, + ee->ee_adc_desired_size_turbo[ee_mode]); + + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, + AR5K_PHY_DESIRED_SIZE_PGA, + ee->ee_pga_desired_size_turbo[ee_mode]); + + /* Tx/Rx margin (Turbo) */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ, + AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX, + ee->ee_margin_tx_rx_turbo[ee_mode]); + + } else { + /* Switch settling time */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING, + AR5K_PHY_SETTLING_SWITCH, + ee->ee_switch_settling[ee_mode]); + + /* Tx/Rx attenuation */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN, + AR5K_PHY_GAIN_TXRX_ATTEN, + ee->ee_atn_tx_rx[ee_mode]); + + /* ADC/PGA desired size */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, + AR5K_PHY_DESIRED_SIZE_ADC, + ee->ee_adc_desired_size[ee_mode]); + + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, + AR5K_PHY_DESIRED_SIZE_PGA, + ee->ee_pga_desired_size[ee_mode]); + + /* Tx/Rx margin */ + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ, + AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX, + ee->ee_margin_tx_rx[ee_mode]); + } + + /* XPA delays */ + ath5k_hw_reg_write(ah, + (ee->ee_tx_end2xpa_disable[ee_mode] << 24) | + (ee->ee_tx_end2xpa_disable[ee_mode] << 16) | + (ee->ee_tx_frm2xpa_enable[ee_mode] << 8) | + (ee->ee_tx_frm2xpa_enable[ee_mode]), AR5K_PHY_RF_CTL4); + + /* XLNA delay */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RF_CTL3, + AR5K_PHY_RF_CTL3_TXE2XLNA_ON, + ee->ee_tx_end2xlna_enable[ee_mode]); + + /* Thresh64 (ANI) */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_NF, + AR5K_PHY_NF_THRESH62, + ee->ee_thr_62[ee_mode]); + + + /* False detect backoff for channels + * that have spur noise. Write the new + * cyclic power RSSI threshold. */ + if (ath5k_hw_chan_has_spur_noise(ah, channel)) + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR, + AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, + AR5K_INIT_CYCRSSI_THR1 + + ee->ee_false_detect[ee_mode]); + else + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR, + AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, + AR5K_INIT_CYCRSSI_THR1); + + /* I/Q correction + * TODO: Per channel i/q infos ? */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, + AR5K_PHY_IQ_CORR_ENABLE | + (ee->ee_i_cal[ee_mode] << AR5K_PHY_IQ_CORR_Q_I_COFF_S) | + ee->ee_q_cal[ee_mode]); + + /* Heavy clipping -disable for now */ + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_1) + ath5k_hw_reg_write(ah, 0, AR5K_PHY_HEAVY_CLIP_ENABLE); + + return; +} + +/* + * Main reset function + */ +int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, + struct ieee80211_channel *channel, bool change_channel) +{ + u32 s_seq[10], s_ant, s_led[3], staid1_flags, tsf_up, tsf_lo; + u32 phy_tst1; + u8 mode, freq, ee_mode, ant[2]; + int i, ret; + + ATH5K_TRACE(ah->ah_sc); + + s_ant = 0; + ee_mode = 0; + staid1_flags = 0; + tsf_up = 0; + tsf_lo = 0; + freq = 0; + mode = 0; + + /* + * Save some registers before a reset + */ + /*DCU/Antenna selection not available on 5210*/ + if (ah->ah_version != AR5K_AR5210) { + + switch (channel->hw_value & CHANNEL_MODES) { + case CHANNEL_A: + mode = AR5K_MODE_11A; + freq = AR5K_INI_RFGAIN_5GHZ; + ee_mode = AR5K_EEPROM_MODE_11A; + break; + case CHANNEL_G: + mode = AR5K_MODE_11G; + freq = AR5K_INI_RFGAIN_2GHZ; + ee_mode = AR5K_EEPROM_MODE_11G; + break; + case CHANNEL_B: + mode = AR5K_MODE_11B; + freq = AR5K_INI_RFGAIN_2GHZ; + ee_mode = AR5K_EEPROM_MODE_11B; + break; + case CHANNEL_T: + mode = AR5K_MODE_11A_TURBO; + freq = AR5K_INI_RFGAIN_5GHZ; + ee_mode = AR5K_EEPROM_MODE_11A; + break; + case CHANNEL_TG: + if (ah->ah_version == AR5K_AR5211) { + ATH5K_ERR(ah->ah_sc, + "TurboG mode not available on 5211"); + return -EINVAL; + } + mode = AR5K_MODE_11G_TURBO; + freq = AR5K_INI_RFGAIN_2GHZ; + ee_mode = AR5K_EEPROM_MODE_11G; + break; + case CHANNEL_XR: + if (ah->ah_version == AR5K_AR5211) { + ATH5K_ERR(ah->ah_sc, + "XR mode not available on 5211"); + return -EINVAL; + } + mode = AR5K_MODE_XR; + freq = AR5K_INI_RFGAIN_5GHZ; + ee_mode = AR5K_EEPROM_MODE_11A; + break; + default: + ATH5K_ERR(ah->ah_sc, + "invalid channel: %d\n", channel->center_freq); + return -EINVAL; + } + + if (change_channel) { + /* + * Save frame sequence count + * For revs. after Oahu, only save + * seq num for DCU 0 (Global seq num) + */ + if (ah->ah_mac_srev < AR5K_SREV_AR5211) { + + for (i = 0; i < 10; i++) + s_seq[i] = ath5k_hw_reg_read(ah, + AR5K_QUEUE_DCU_SEQNUM(i)); + + } else { + s_seq[0] = ath5k_hw_reg_read(ah, + AR5K_QUEUE_DCU_SEQNUM(0)); + } + + /* TSF accelerates on AR5211 durring reset + * As a workaround save it here and restore + * it later so that it's back in time after + * reset. This way it'll get re-synced on the + * next beacon without breaking ad-hoc. + * + * On AR5212 TSF is almost preserved across a + * reset so it stays back in time anyway and + * we don't have to save/restore it. + * + * XXX: Since this breaks power saving we have + * to disable power saving until we receive the + * next beacon, so we can resync beacon timers */ + if (ah->ah_version == AR5K_AR5211) { + tsf_up = ath5k_hw_reg_read(ah, AR5K_TSF_U32); + tsf_lo = ath5k_hw_reg_read(ah, AR5K_TSF_L32); + } + } + + /* Save default antenna */ + s_ant = ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA); + + if (ah->ah_version == AR5K_AR5212) { + /* Restore normal 32/40MHz clock operation + * to avoid register access delay on certain + * PHY registers */ + ath5k_hw_set_sleep_clock(ah, false); + + /* Since we are going to write rf buffer + * check if we have any pending gain_F + * optimization settings */ + if (change_channel && ah->ah_rf_banks != NULL) + ath5k_hw_gainf_calibrate(ah); + } + } + + /*GPIOs*/ + s_led[0] = ath5k_hw_reg_read(ah, AR5K_PCICFG) & + AR5K_PCICFG_LEDSTATE; + s_led[1] = ath5k_hw_reg_read(ah, AR5K_GPIOCR); + s_led[2] = ath5k_hw_reg_read(ah, AR5K_GPIODO); + + /* AR5K_STA_ID1 flags, only preserve antenna + * settings and ack/cts rate mode */ + staid1_flags = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & + (AR5K_STA_ID1_DEFAULT_ANTENNA | + AR5K_STA_ID1_DESC_ANTENNA | + AR5K_STA_ID1_RTS_DEF_ANTENNA | + AR5K_STA_ID1_ACKCTS_6MB | + AR5K_STA_ID1_BASE_RATE_11B | + AR5K_STA_ID1_SELFGEN_DEF_ANT); + + /* Wakeup the device */ + ret = ath5k_hw_nic_wakeup(ah, channel->hw_value, false); + if (ret) + return ret; + + /* + * Initialize operating mode + */ + ah->ah_op_mode = op_mode; + + /* PHY access enable */ + if (ah->ah_mac_srev >= AR5K_SREV_AR5211) + ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); + else + ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ | 0x40, + AR5K_PHY(0)); + + /* Write initial settings */ + ret = ath5k_hw_write_initvals(ah, mode, change_channel); + if (ret) + return ret; + + /* + * 5211/5212 Specific + */ + if (ah->ah_version != AR5K_AR5210) { + + /* + * Write initial RF gain settings + * This should work for both 5111/5112 + */ + ret = ath5k_hw_rfgain_init(ah, freq); + if (ret) + return ret; + + mdelay(1); + + /* + * Tweak initval settings for revised + * chipsets and add some more config + * bits + */ + ath5k_hw_tweak_initval_settings(ah, channel); + + /* + * Set TX power + */ + ret = ath5k_hw_txpower(ah, channel, ee_mode, + ah->ah_txpower.txp_max_pwr / 2); + if (ret) + return ret; + + /* Write rate duration table only on AR5212 and if + * virtual interface has already been brought up + * XXX: rethink this after new mode changes to + * mac80211 are integrated */ + if (ah->ah_version == AR5K_AR5212 && + ah->ah_sc->vif != NULL) + ath5k_hw_write_rate_duration(ah, mode); + + /* + * Write RF buffer + */ + ret = ath5k_hw_rfregs_init(ah, channel, mode); + if (ret) + return ret; + + + /* Write OFDM timings on 5212*/ + if (ah->ah_version == AR5K_AR5212 && + channel->hw_value & CHANNEL_OFDM) { + struct ath5k_eeprom_info *ee = + &ah->ah_capabilities.cap_eeprom; + + ret = ath5k_hw_write_ofdm_timings(ah, channel); + if (ret) + return ret; + + /* Note: According to docs we can have a newer + * EEPROM on old hardware, so we need to verify + * that our hardware is new enough to have spur + * mitigation registers (delta phase etc) */ + if (ah->ah_mac_srev >= AR5K_SREV_AR5424 || + (ah->ah_mac_srev >= AR5K_SREV_AR5424 && + ee->ee_version >= AR5K_EEPROM_VERSION_5_3)) + ath5k_hw_set_spur_mitigation_filter(ah, + channel); + } + + /*Enable/disable 802.11b mode on 5111 + (enable 2111 frequency converter + CCK)*/ + if (ah->ah_radio == AR5K_RF5111) { + if (mode == AR5K_MODE_11B) + AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, + AR5K_TXCFG_B_MODE); + else + AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG, + AR5K_TXCFG_B_MODE); + } + + /* + * In case a fixed antenna was set as default + * use the same switch table twice. + */ + if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_A) + ant[0] = ant[1] = AR5K_ANT_SWTABLE_A; + else if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_B) + ant[0] = ant[1] = AR5K_ANT_SWTABLE_B; + else { + ant[0] = AR5K_ANT_SWTABLE_A; + ant[1] = AR5K_ANT_SWTABLE_B; + } + + /* Commit values from EEPROM */ + ath5k_hw_commit_eeprom_settings(ah, channel, ant, ee_mode); + + } else { + /* + * For 5210 we do all initialization using + * initvals, so we don't have to modify + * any settings (5210 also only supports + * a/aturbo modes) + */ + mdelay(1); + /* Disable phy and wait */ + ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); + mdelay(1); + } + + /* + * Restore saved values + */ + + /*DCU/Antenna selection not available on 5210*/ + if (ah->ah_version != AR5K_AR5210) { + + if (change_channel) { + if (ah->ah_mac_srev < AR5K_SREV_AR5211) { + for (i = 0; i < 10; i++) + ath5k_hw_reg_write(ah, s_seq[i], + AR5K_QUEUE_DCU_SEQNUM(i)); + } else { + ath5k_hw_reg_write(ah, s_seq[0], + AR5K_QUEUE_DCU_SEQNUM(0)); + } + + + if (ah->ah_version == AR5K_AR5211) { + ath5k_hw_reg_write(ah, tsf_up, AR5K_TSF_U32); + ath5k_hw_reg_write(ah, tsf_lo, AR5K_TSF_L32); + } + } + + ath5k_hw_reg_write(ah, s_ant, AR5K_DEFAULT_ANTENNA); + } + + /* Ledstate */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, s_led[0]); + + /* Gpio settings */ + ath5k_hw_reg_write(ah, s_led[1], AR5K_GPIOCR); + ath5k_hw_reg_write(ah, s_led[2], AR5K_GPIODO); + + /* Restore sta_id flags and preserve our mac address*/ + ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_sta_id), + AR5K_STA_ID0); + ath5k_hw_reg_write(ah, staid1_flags | AR5K_HIGH_ID(ah->ah_sta_id), + AR5K_STA_ID1); + + + /* + * Configure PCU + */ + + /* Restore bssid and bssid mask */ + /* XXX: add ah->aid once mac80211 gives this to us */ + ath5k_hw_set_associd(ah, ah->ah_bssid, 0); + + /* Set PCU config */ + ath5k_hw_set_opmode(ah); + + /* Clear any pending interrupts + * PISR/SISR Not available on 5210 */ + if (ah->ah_version != AR5K_AR5210) + ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR); + + /* Set RSSI/BRSSI thresholds + * + * Note: If we decide to set this value + * dynamicaly, have in mind that when AR5K_RSSI_THR + * register is read it might return 0x40 if we haven't + * wrote anything to it plus BMISS RSSI threshold is zeroed. + * So doing a save/restore procedure here isn't the right + * choice. Instead store it on ath5k_hw */ + ath5k_hw_reg_write(ah, (AR5K_TUNE_RSSI_THRES | + AR5K_TUNE_BMISS_THRES << + AR5K_RSSI_THR_BMISS_S), + AR5K_RSSI_THR); + + /* MIC QoS support */ + if (ah->ah_mac_srev >= AR5K_SREV_AR2413) { + ath5k_hw_reg_write(ah, 0x000100aa, AR5K_MIC_QOS_CTL); + ath5k_hw_reg_write(ah, 0x00003210, AR5K_MIC_QOS_SEL); + } + + /* QoS NOACK Policy */ + if (ah->ah_version == AR5K_AR5212) { + ath5k_hw_reg_write(ah, + AR5K_REG_SM(2, AR5K_QOS_NOACK_2BIT_VALUES) | + AR5K_REG_SM(5, AR5K_QOS_NOACK_BIT_OFFSET) | + AR5K_REG_SM(0, AR5K_QOS_NOACK_BYTE_OFFSET), + AR5K_QOS_NOACK); + } + + + /* + * Configure PHY + */ + + /* Set channel on PHY */ + ret = ath5k_hw_channel(ah, channel); + if (ret) + return ret; + + /* + * Enable the PHY and wait until completion + * This includes BaseBand and Synthesizer + * activation. + */ + ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); + + /* + * On 5211+ read activation -> rx delay + * and use it. + * + * TODO: Half/quarter rate support + */ + if (ah->ah_version != AR5K_AR5210) { + u32 delay; + delay = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) & + AR5K_PHY_RX_DELAY_M; + delay = (channel->hw_value & CHANNEL_CCK) ? + ((delay << 2) / 22) : (delay / 10); + + udelay(100 + (2 * delay)); + } else { + mdelay(1); + } + + /* + * Perform ADC test to see if baseband is ready + * Set tx hold and check adc test register + */ + phy_tst1 = ath5k_hw_reg_read(ah, AR5K_PHY_TST1); + ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, AR5K_PHY_TST1); + for (i = 0; i <= 20; i++) { + if (!(ath5k_hw_reg_read(ah, AR5K_PHY_ADC_TEST) & 0x10)) + break; + udelay(200); + } + ath5k_hw_reg_write(ah, phy_tst1, AR5K_PHY_TST1); + + /* + * Start automatic gain control calibration + * + * During AGC calibration RX path is re-routed to + * a power detector so we don't receive anything. + * + * This method is used to calibrate some static offsets + * used together with on-the fly I/Q calibration (the + * one performed via ath5k_hw_phy_calibrate), that doesn't + * interrupt rx path. + * + * While rx path is re-routed to the power detector we also + * start a noise floor calibration, to measure the + * card's noise floor (the noise we measure when we are not + * transmiting or receiving anything). + * + * If we are in a noisy environment AGC calibration may time + * out and/or noise floor calibration might timeout. + */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_CAL); + + /* At the same time start I/Q calibration for QAM constellation + * -no need for CCK- */ + ah->ah_calibration = false; + if (!(mode == AR5K_MODE_11B)) { + ah->ah_calibration = true; + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, + AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, + AR5K_PHY_IQ_RUN); + } + + /* Wait for gain calibration to finish (we check for I/Q calibration + * during ath5k_phy_calibrate) */ + if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_CAL, 0, false)) { + ATH5K_ERR(ah->ah_sc, "gain calibration timeout (%uMHz)\n", + channel->center_freq); + } + + /* + * If we run NF calibration before AGC, it always times out. + * Binary HAL starts NF and AGC calibration at the same time + * and only waits for AGC to finish. Also if AGC or NF cal. + * times out, reset doesn't fail on binary HAL. I believe + * that's wrong because since rx path is routed to a detector, + * if cal. doesn't finish we won't have RX. Sam's HAL for AR5210/5211 + * enables noise floor calibration after offset calibration and if noise + * floor calibration fails, reset fails. I believe that's + * a better approach, we just need to find a polling interval + * that suits best, even if reset continues we need to make + * sure that rx path is ready. + */ + ath5k_hw_noise_floor_calibration(ah, channel->center_freq); + + /* Restore antenna mode */ + ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); + + /* + * Configure QCUs/DCUs + */ + + /* TODO: HW Compression support for data queues */ + /* TODO: Burst prefetch for data queues */ + + /* + * Reset queues and start beacon timers at the end of the reset routine + * This also sets QCU mask on each DCU for 1:1 qcu to dcu mapping + * Note: If we want we can assign multiple qcus on one dcu. + */ + for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) { + ret = ath5k_hw_reset_tx_queue(ah, i); + if (ret) { + ATH5K_ERR(ah->ah_sc, + "failed to reset TX queue #%d\n", i); + return ret; + } + } + + + /* + * Configure DMA/Interrupts + */ + + /* + * Set Rx/Tx DMA Configuration + * + * Set standard DMA size (128). Note that + * a DMA size of 512 causes rx overruns and tx errors + * on pci-e cards (tested on 5424 but since rx overruns + * also occur on 5416/5418 with madwifi we set 128 + * for all PCI-E cards to be safe). + * + * XXX: need to check 5210 for this + * TODO: Check out tx triger level, it's always 64 on dumps but I + * guess we can tweak it and see how it goes ;-) + */ + if (ah->ah_version != AR5K_AR5210) { + AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, + AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B); + AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG, + AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B); + } + + /* Pre-enable interrupts on 5211/5212*/ + if (ah->ah_version != AR5K_AR5210) + ath5k_hw_set_imr(ah, ah->ah_imr); + + /* Enable 32KHz clock function for AR5212+ chips + * Set clocks to 32KHz operation and use an + * external 32KHz crystal when sleeping if one + * exists */ + if (ah->ah_version == AR5K_AR5212) + ath5k_hw_set_sleep_clock(ah, true); + + /* + * Disable beacons and reset the register + */ + AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE | + AR5K_BEACON_RESET_TSF); + + return 0; +} + +#undef _ATH5K_RESET diff --git a/drivers/net/wireless/ath/ath5k/rfbuffer.h b/drivers/net/wireless/ath/ath5k/rfbuffer.h new file mode 100644 index 00000000000..e50baff6617 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/rfbuffer.h @@ -0,0 +1,1181 @@ +/* + * RF Buffer handling functions + * + * Copyright (c) 2009 Nick Kossifidis <mickflemm@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + + +/* + * There are some special registers on the RF chip + * that control various operation settings related mostly to + * the analog parts (channel, gain adjustment etc). + * + * We don't write on those registers directly but + * we send a data packet on the chip, using a special register, + * that holds all the settings we need. After we 've sent the + * data packet, we write on another special register to notify hw + * to apply the settings. This is done so that control registers + * can be dynamicaly programmed during operation and the settings + * are applied faster on the hw. + * + * We call each data packet an "RF Bank" and all the data we write + * (all RF Banks) "RF Buffer". This file holds initial RF Buffer + * data for the different RF chips, and various info to match RF + * Buffer offsets with specific RF registers so that we can access + * them. We tweak these settings on rfregs_init function. + * + * Also check out reg.h and U.S. Patent 6677779 B1 (about buffer + * registers and control registers): + * + * http://www.google.com/patents?id=qNURAAAAEBAJ + */ + + +/* + * Struct to hold default mode specific RF + * register values (RF Banks) + */ +struct ath5k_ini_rfbuffer { + u8 rfb_bank; /* RF Bank number */ + u16 rfb_ctrl_register; /* RF Buffer control register */ + u32 rfb_mode_data[5]; /* RF Buffer data for each mode */ +}; + +/* + * Struct to hold RF Buffer field + * infos used to access certain RF + * analog registers + */ +struct ath5k_rfb_field { + u8 len; /* Field length */ + u16 pos; /* Offset on the raw packet */ + u8 col; /* Column -used for shifting */ +}; + +/* + * RF analog register definition + */ +struct ath5k_rf_reg { + u8 bank; /* RF Buffer Bank number */ + u8 index; /* Register's index on rf_regs_idx */ + struct ath5k_rfb_field field; /* RF Buffer field for this register */ +}; + +/* Map RF registers to indexes + * We do this to handle common bits and make our + * life easier by using an index for each register + * instead of a full rfb_field */ +enum ath5k_rf_regs_idx { + /* BANK 6 */ + AR5K_RF_OB_2GHZ = 0, + AR5K_RF_OB_5GHZ, + AR5K_RF_DB_2GHZ, + AR5K_RF_DB_5GHZ, + AR5K_RF_FIXED_BIAS_A, + AR5K_RF_FIXED_BIAS_B, + AR5K_RF_PWD_XPD, + AR5K_RF_XPD_SEL, + AR5K_RF_XPD_GAIN, + AR5K_RF_PD_GAIN_LO, + AR5K_RF_PD_GAIN_HI, + AR5K_RF_HIGH_VC_CP, + AR5K_RF_MID_VC_CP, + AR5K_RF_LOW_VC_CP, + AR5K_RF_PUSH_UP, + AR5K_RF_PAD2GND, + AR5K_RF_XB2_LVL, + AR5K_RF_XB5_LVL, + AR5K_RF_PWD_ICLOBUF_2G, + AR5K_RF_PWD_84, + AR5K_RF_PWD_90, + AR5K_RF_PWD_130, + AR5K_RF_PWD_131, + AR5K_RF_PWD_132, + AR5K_RF_PWD_136, + AR5K_RF_PWD_137, + AR5K_RF_PWD_138, + AR5K_RF_PWD_166, + AR5K_RF_PWD_167, + AR5K_RF_DERBY_CHAN_SEL_MODE, + /* BANK 7 */ + AR5K_RF_GAIN_I, + AR5K_RF_PLO_SEL, + AR5K_RF_RFGAIN_SEL, + AR5K_RF_RFGAIN_STEP, + AR5K_RF_WAIT_S, + AR5K_RF_WAIT_I, + AR5K_RF_MAX_TIME, + AR5K_RF_MIXVGA_OVR, + AR5K_RF_MIXGAIN_OVR, + AR5K_RF_MIXGAIN_STEP, + AR5K_RF_PD_DELAY_A, + AR5K_RF_PD_DELAY_B, + AR5K_RF_PD_DELAY_XR, + AR5K_RF_PD_PERIOD_A, + AR5K_RF_PD_PERIOD_B, + AR5K_RF_PD_PERIOD_XR, +}; + + +/*******************\ +* RF5111 (Sombrero) * +\*******************/ + +/* BANK 6 len pos col */ +#define AR5K_RF5111_OB_2GHZ { 3, 119, 0 } +#define AR5K_RF5111_DB_2GHZ { 3, 122, 0 } + +#define AR5K_RF5111_OB_5GHZ { 3, 104, 0 } +#define AR5K_RF5111_DB_5GHZ { 3, 107, 0 } + +#define AR5K_RF5111_PWD_XPD { 1, 95, 0 } +#define AR5K_RF5111_XPD_GAIN { 4, 96, 0 } + +/* Access to PWD registers */ +#define AR5K_RF5111_PWD(_n) { 1, (135 - _n), 3 } + +/* BANK 7 len pos col */ +#define AR5K_RF5111_GAIN_I { 6, 29, 0 } +#define AR5K_RF5111_PLO_SEL { 1, 4, 0 } +#define AR5K_RF5111_RFGAIN_SEL { 1, 36, 0 } +#define AR5K_RF5111_RFGAIN_STEP { 6, 37, 0 } +/* Only on AR5212 BaseBand and up */ +#define AR5K_RF5111_WAIT_S { 5, 19, 0 } +#define AR5K_RF5111_WAIT_I { 5, 24, 0 } +#define AR5K_RF5111_MAX_TIME { 2, 49, 0 } + +static const struct ath5k_rf_reg rf_regs_5111[] = { + {6, AR5K_RF_OB_2GHZ, AR5K_RF5111_OB_2GHZ}, + {6, AR5K_RF_DB_2GHZ, AR5K_RF5111_DB_2GHZ}, + {6, AR5K_RF_OB_5GHZ, AR5K_RF5111_OB_5GHZ}, + {6, AR5K_RF_DB_5GHZ, AR5K_RF5111_DB_5GHZ}, + {6, AR5K_RF_PWD_XPD, AR5K_RF5111_PWD_XPD}, + {6, AR5K_RF_XPD_GAIN, AR5K_RF5111_XPD_GAIN}, + {6, AR5K_RF_PWD_84, AR5K_RF5111_PWD(84)}, + {6, AR5K_RF_PWD_90, AR5K_RF5111_PWD(90)}, + {7, AR5K_RF_GAIN_I, AR5K_RF5111_GAIN_I}, + {7, AR5K_RF_PLO_SEL, AR5K_RF5111_PLO_SEL}, + {7, AR5K_RF_RFGAIN_SEL, AR5K_RF5111_RFGAIN_SEL}, + {7, AR5K_RF_RFGAIN_STEP, AR5K_RF5111_RFGAIN_STEP}, + {7, AR5K_RF_WAIT_S, AR5K_RF5111_WAIT_S}, + {7, AR5K_RF_WAIT_I, AR5K_RF5111_WAIT_I}, + {7, AR5K_RF_MAX_TIME, AR5K_RF5111_MAX_TIME} +}; + +/* Default mode specific settings */ +static const struct ath5k_ini_rfbuffer rfb_5111[] = { + { 0, 0x989c, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00380000, 0x00380000, 0x00380000, 0x00380000, 0x00380000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x000000c0, 0x00000080, 0x00000080 } }, + { 0, 0x989c, + { 0x000400f9, 0x000400f9, 0x000400ff, 0x000400fd, 0x000400fd } }, + { 0, 0x98d4, + { 0x00000000, 0x00000000, 0x00000004, 0x00000004, 0x00000004 } }, + { 1, 0x98d4, + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d4, + { 0x00000010, 0x00000014, 0x00000010, 0x00000010, 0x00000014 } }, + { 3, 0x98d8, + { 0x00601068, 0x00601068, 0x00601068, 0x00601068, 0x00601068 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } }, + { 6, 0x989c, + { 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x0a000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x003800c0, 0x00380080, 0x023800c0, 0x003800c0, 0x003800c0 } }, + { 6, 0x989c, + { 0x00020006, 0x00020006, 0x00000006, 0x00020006, 0x00020006 } }, + { 6, 0x989c, + { 0x00000089, 0x00000089, 0x00000089, 0x00000089, 0x00000089 } }, + { 6, 0x989c, + { 0x000000a0, 0x000000a0, 0x000000a0, 0x000000a0, 0x000000a0 } }, + { 6, 0x989c, + { 0x00040007, 0x00040007, 0x00040007, 0x00040007, 0x00040007 } }, + { 6, 0x98d4, + { 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a } }, + { 7, 0x989c, + { 0x00000040, 0x00000048, 0x00000040, 0x00000040, 0x00000040 } }, + { 7, 0x989c, + { 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 } }, + { 7, 0x989c, + { 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008 } }, + { 7, 0x989c, + { 0x0000004f, 0x0000004f, 0x0000004f, 0x0000004f, 0x0000004f } }, + { 7, 0x989c, + { 0x000000f1, 0x000000f1, 0x00000061, 0x000000f1, 0x000000f1 } }, + { 7, 0x989c, + { 0x0000904f, 0x0000904f, 0x0000904c, 0x0000904f, 0x0000904f } }, + { 7, 0x989c, + { 0x0000125a, 0x0000125a, 0x0000129a, 0x0000125a, 0x0000125a } }, + { 7, 0x98cc, + { 0x0000000e, 0x0000000e, 0x0000000f, 0x0000000e, 0x0000000e } }, +}; + + + +/***********************\ +* RF5112/RF2112 (Derby) * +\***********************/ + +/* BANK 7 (Common) len pos col */ +#define AR5K_RF5112X_GAIN_I { 6, 14, 0 } +#define AR5K_RF5112X_MIXVGA_OVR { 1, 36, 0 } +#define AR5K_RF5112X_MIXGAIN_OVR { 2, 37, 0 } +#define AR5K_RF5112X_MIXGAIN_STEP { 4, 32, 0 } +#define AR5K_RF5112X_PD_DELAY_A { 4, 58, 0 } +#define AR5K_RF5112X_PD_DELAY_B { 4, 62, 0 } +#define AR5K_RF5112X_PD_DELAY_XR { 4, 66, 0 } +#define AR5K_RF5112X_PD_PERIOD_A { 4, 70, 0 } +#define AR5K_RF5112X_PD_PERIOD_B { 4, 74, 0 } +#define AR5K_RF5112X_PD_PERIOD_XR { 4, 78, 0 } + +/* RFX112 (Derby 1) */ + +/* BANK 6 len pos col */ +#define AR5K_RF5112_OB_2GHZ { 3, 269, 0 } +#define AR5K_RF5112_DB_2GHZ { 3, 272, 0 } + +#define AR5K_RF5112_OB_5GHZ { 3, 261, 0 } +#define AR5K_RF5112_DB_5GHZ { 3, 264, 0 } + +#define AR5K_RF5112_FIXED_BIAS_A { 1, 260, 0 } +#define AR5K_RF5112_FIXED_BIAS_B { 1, 259, 0 } + +#define AR5K_RF5112_XPD_SEL { 1, 284, 0 } +#define AR5K_RF5112_XPD_GAIN { 2, 252, 0 } + +/* Access to PWD registers */ +#define AR5K_RF5112_PWD(_n) { 1, (302 - _n), 3 } + +static const struct ath5k_rf_reg rf_regs_5112[] = { + {6, AR5K_RF_OB_2GHZ, AR5K_RF5112_OB_2GHZ}, + {6, AR5K_RF_DB_2GHZ, AR5K_RF5112_DB_2GHZ}, + {6, AR5K_RF_OB_5GHZ, AR5K_RF5112_OB_5GHZ}, + {6, AR5K_RF_DB_5GHZ, AR5K_RF5112_DB_5GHZ}, + {6, AR5K_RF_FIXED_BIAS_A, AR5K_RF5112_FIXED_BIAS_A}, + {6, AR5K_RF_FIXED_BIAS_B, AR5K_RF5112_FIXED_BIAS_B}, + {6, AR5K_RF_XPD_SEL, AR5K_RF5112_XPD_SEL}, + {6, AR5K_RF_XPD_GAIN, AR5K_RF5112_XPD_GAIN}, + {6, AR5K_RF_PWD_130, AR5K_RF5112_PWD(130)}, + {6, AR5K_RF_PWD_131, AR5K_RF5112_PWD(131)}, + {6, AR5K_RF_PWD_132, AR5K_RF5112_PWD(132)}, + {6, AR5K_RF_PWD_136, AR5K_RF5112_PWD(136)}, + {6, AR5K_RF_PWD_137, AR5K_RF5112_PWD(137)}, + {6, AR5K_RF_PWD_138, AR5K_RF5112_PWD(138)}, + {7, AR5K_RF_GAIN_I, AR5K_RF5112X_GAIN_I}, + {7, AR5K_RF_MIXVGA_OVR, AR5K_RF5112X_MIXVGA_OVR}, + {7, AR5K_RF_MIXGAIN_OVR, AR5K_RF5112X_MIXGAIN_OVR}, + {7, AR5K_RF_MIXGAIN_STEP, AR5K_RF5112X_MIXGAIN_STEP}, + {7, AR5K_RF_PD_DELAY_A, AR5K_RF5112X_PD_DELAY_A}, + {7, AR5K_RF_PD_DELAY_B, AR5K_RF5112X_PD_DELAY_B}, + {7, AR5K_RF_PD_DELAY_XR, AR5K_RF5112X_PD_DELAY_XR}, + {7, AR5K_RF_PD_PERIOD_A, AR5K_RF5112X_PD_PERIOD_A}, + {7, AR5K_RF_PD_PERIOD_B, AR5K_RF5112X_PD_PERIOD_B}, + {7, AR5K_RF_PD_PERIOD_XR, AR5K_RF5112X_PD_PERIOD_XR}, +}; + +/* Default mode specific settings */ +static const struct ath5k_ini_rfbuffer rfb_5112[] = { + { 1, 0x98d4, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d0, + { 0x03060408, 0x03070408, 0x03060408, 0x03060408, 0x03070408 } }, + { 3, 0x98dc, + { 0x00a0c0c0, 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0, 0x00e0c0c0 } }, + { 6, 0x989c, + { 0x00a00000, 0x00a00000, 0x00a00000, 0x00a00000, 0x00a00000 } }, + { 6, 0x989c, + { 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00660000, 0x00660000, 0x00660000, 0x00660000, 0x00660000 } }, + { 6, 0x989c, + { 0x00db0000, 0x00db0000, 0x00db0000, 0x00db0000, 0x00db0000 } }, + { 6, 0x989c, + { 0x00f10000, 0x00f10000, 0x00f10000, 0x00f10000, 0x00f10000 } }, + { 6, 0x989c, + { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } }, + { 6, 0x989c, + { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } }, + { 6, 0x989c, + { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } }, + { 6, 0x989c, + { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, + { 6, 0x989c, + { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, + { 6, 0x989c, + { 0x008b0000, 0x008b0000, 0x008b0000, 0x008b0000, 0x008b0000 } }, + { 6, 0x989c, + { 0x00600000, 0x00600000, 0x00600000, 0x00600000, 0x00600000 } }, + { 6, 0x989c, + { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } }, + { 6, 0x989c, + { 0x00840000, 0x00840000, 0x00840000, 0x00840000, 0x00840000 } }, + { 6, 0x989c, + { 0x00640000, 0x00640000, 0x00640000, 0x00640000, 0x00640000 } }, + { 6, 0x989c, + { 0x00200000, 0x00200000, 0x00200000, 0x00200000, 0x00200000 } }, + { 6, 0x989c, + { 0x00240000, 0x00240000, 0x00240000, 0x00240000, 0x00240000 } }, + { 6, 0x989c, + { 0x00250000, 0x00250000, 0x00250000, 0x00250000, 0x00250000 } }, + { 6, 0x989c, + { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } }, + { 6, 0x989c, + { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } }, + { 6, 0x989c, + { 0x00510000, 0x00510000, 0x00510000, 0x00510000, 0x00510000 } }, + { 6, 0x989c, + { 0x1c040000, 0x1c040000, 0x1c040000, 0x1c040000, 0x1c040000 } }, + { 6, 0x989c, + { 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000 } }, + { 6, 0x989c, + { 0x00a10000, 0x00a10000, 0x00a10000, 0x00a10000, 0x00a10000 } }, + { 6, 0x989c, + { 0x00400000, 0x00400000, 0x00400000, 0x00400000, 0x00400000 } }, + { 6, 0x989c, + { 0x03090000, 0x03090000, 0x03090000, 0x03090000, 0x03090000 } }, + { 6, 0x989c, + { 0x06000000, 0x06000000, 0x06000000, 0x06000000, 0x06000000 } }, + { 6, 0x989c, + { 0x000000b0, 0x000000b0, 0x000000a8, 0x000000a8, 0x000000a8 } }, + { 6, 0x989c, + { 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e } }, + { 6, 0x989c, + { 0x006c4a41, 0x006c4a41, 0x006c4af1, 0x006c4a61, 0x006c4a61 } }, + { 6, 0x989c, + { 0x0050892a, 0x0050892a, 0x0050892b, 0x0050892b, 0x0050892b } }, + { 6, 0x989c, + { 0x00842400, 0x00842400, 0x00842400, 0x00842400, 0x00842400 } }, + { 6, 0x989c, + { 0x00c69200, 0x00c69200, 0x00c69200, 0x00c69200, 0x00c69200 } }, + { 6, 0x98d0, + { 0x0002000c, 0x0002000c, 0x0002000c, 0x0002000c, 0x0002000c } }, + { 7, 0x989c, + { 0x00000094, 0x00000094, 0x00000094, 0x00000094, 0x00000094 } }, + { 7, 0x989c, + { 0x00000091, 0x00000091, 0x00000091, 0x00000091, 0x00000091 } }, + { 7, 0x989c, + { 0x0000000a, 0x0000000a, 0x00000012, 0x00000012, 0x00000012 } }, + { 7, 0x989c, + { 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } }, + { 7, 0x989c, + { 0x000000c1, 0x000000c1, 0x000000c1, 0x000000c1, 0x000000c1 } }, + { 7, 0x989c, + { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } }, + { 7, 0x989c, + { 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0 } }, + { 7, 0x989c, + { 0x00000022, 0x00000022, 0x00000022, 0x00000022, 0x00000022 } }, + { 7, 0x989c, + { 0x00000092, 0x00000092, 0x00000092, 0x00000092, 0x00000092 } }, + { 7, 0x989c, + { 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4 } }, + { 7, 0x989c, + { 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc } }, + { 7, 0x989c, + { 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c } }, + { 7, 0x98c4, + { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } }, +}; + +/* RFX112A (Derby 2) */ + +/* BANK 6 len pos col */ +#define AR5K_RF5112A_OB_2GHZ { 3, 287, 0 } +#define AR5K_RF5112A_DB_2GHZ { 3, 290, 0 } + +#define AR5K_RF5112A_OB_5GHZ { 3, 279, 0 } +#define AR5K_RF5112A_DB_5GHZ { 3, 282, 0 } + +#define AR5K_RF5112A_FIXED_BIAS_A { 1, 278, 0 } +#define AR5K_RF5112A_FIXED_BIAS_B { 1, 277, 0 } + +#define AR5K_RF5112A_XPD_SEL { 1, 302, 0 } +#define AR5K_RF5112A_PDGAINLO { 2, 270, 0 } +#define AR5K_RF5112A_PDGAINHI { 2, 257, 0 } + +/* Access to PWD registers */ +#define AR5K_RF5112A_PWD(_n) { 1, (306 - _n), 3 } + +/* Voltage regulators */ +#define AR5K_RF5112A_HIGH_VC_CP { 2, 90, 2 } +#define AR5K_RF5112A_MID_VC_CP { 2, 92, 2 } +#define AR5K_RF5112A_LOW_VC_CP { 2, 94, 2 } +#define AR5K_RF5112A_PUSH_UP { 1, 254, 2 } + +/* Power consumption */ +#define AR5K_RF5112A_PAD2GND { 1, 281, 1 } +#define AR5K_RF5112A_XB2_LVL { 2, 1, 3 } +#define AR5K_RF5112A_XB5_LVL { 2, 3, 3 } + +static const struct ath5k_rf_reg rf_regs_5112a[] = { + {6, AR5K_RF_OB_2GHZ, AR5K_RF5112A_OB_2GHZ}, + {6, AR5K_RF_DB_2GHZ, AR5K_RF5112A_DB_2GHZ}, + {6, AR5K_RF_OB_5GHZ, AR5K_RF5112A_OB_5GHZ}, + {6, AR5K_RF_DB_5GHZ, AR5K_RF5112A_DB_5GHZ}, + {6, AR5K_RF_FIXED_BIAS_A, AR5K_RF5112A_FIXED_BIAS_A}, + {6, AR5K_RF_FIXED_BIAS_B, AR5K_RF5112A_FIXED_BIAS_B}, + {6, AR5K_RF_XPD_SEL, AR5K_RF5112A_XPD_SEL}, + {6, AR5K_RF_PD_GAIN_LO, AR5K_RF5112A_PDGAINLO}, + {6, AR5K_RF_PD_GAIN_HI, AR5K_RF5112A_PDGAINHI}, + {6, AR5K_RF_PWD_130, AR5K_RF5112A_PWD(130)}, + {6, AR5K_RF_PWD_131, AR5K_RF5112A_PWD(131)}, + {6, AR5K_RF_PWD_132, AR5K_RF5112A_PWD(132)}, + {6, AR5K_RF_PWD_136, AR5K_RF5112A_PWD(136)}, + {6, AR5K_RF_PWD_137, AR5K_RF5112A_PWD(137)}, + {6, AR5K_RF_PWD_138, AR5K_RF5112A_PWD(138)}, + {6, AR5K_RF_PWD_166, AR5K_RF5112A_PWD(166)}, + {6, AR5K_RF_PWD_167, AR5K_RF5112A_PWD(167)}, + {6, AR5K_RF_HIGH_VC_CP, AR5K_RF5112A_HIGH_VC_CP}, + {6, AR5K_RF_MID_VC_CP, AR5K_RF5112A_MID_VC_CP}, + {6, AR5K_RF_LOW_VC_CP, AR5K_RF5112A_LOW_VC_CP}, + {6, AR5K_RF_PUSH_UP, AR5K_RF5112A_PUSH_UP}, + {6, AR5K_RF_PAD2GND, AR5K_RF5112A_PAD2GND}, + {6, AR5K_RF_XB2_LVL, AR5K_RF5112A_XB2_LVL}, + {6, AR5K_RF_XB5_LVL, AR5K_RF5112A_XB5_LVL}, + {7, AR5K_RF_GAIN_I, AR5K_RF5112X_GAIN_I}, + {7, AR5K_RF_MIXVGA_OVR, AR5K_RF5112X_MIXVGA_OVR}, + {7, AR5K_RF_MIXGAIN_OVR, AR5K_RF5112X_MIXGAIN_OVR}, + {7, AR5K_RF_MIXGAIN_STEP, AR5K_RF5112X_MIXGAIN_STEP}, + {7, AR5K_RF_PD_DELAY_A, AR5K_RF5112X_PD_DELAY_A}, + {7, AR5K_RF_PD_DELAY_B, AR5K_RF5112X_PD_DELAY_B}, + {7, AR5K_RF_PD_DELAY_XR, AR5K_RF5112X_PD_DELAY_XR}, + {7, AR5K_RF_PD_PERIOD_A, AR5K_RF5112X_PD_PERIOD_A}, + {7, AR5K_RF_PD_PERIOD_B, AR5K_RF5112X_PD_PERIOD_B}, + {7, AR5K_RF_PD_PERIOD_XR, AR5K_RF5112X_PD_PERIOD_XR}, +}; + +/* Default mode specific settings */ +static const struct ath5k_ini_rfbuffer rfb_5112a[] = { + { 1, 0x98d4, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d0, + { 0x03060408, 0x03070408, 0x03060408, 0x03060408, 0x03070408 } }, + { 3, 0x98dc, + { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } }, + { 6, 0x989c, + { 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00800000, 0x00800000, 0x00800000, 0x00800000, 0x00800000 } }, + { 6, 0x989c, + { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } }, + { 6, 0x989c, + { 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00180000, 0x00180000, 0x00180000, 0x00180000, 0x00180000 } }, + { 6, 0x989c, + { 0x00600000, 0x00600000, 0x006e0000, 0x006e0000, 0x006e0000 } }, + { 6, 0x989c, + { 0x00c70000, 0x00c70000, 0x00c70000, 0x00c70000, 0x00c70000 } }, + { 6, 0x989c, + { 0x004b0000, 0x004b0000, 0x004b0000, 0x004b0000, 0x004b0000 } }, + { 6, 0x989c, + { 0x04480000, 0x04480000, 0x04480000, 0x04480000, 0x04480000 } }, + { 6, 0x989c, + { 0x004c0000, 0x004c0000, 0x004c0000, 0x004c0000, 0x004c0000 } }, + { 6, 0x989c, + { 0x00e40000, 0x00e40000, 0x00e40000, 0x00e40000, 0x00e40000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00fc0000, 0x00fc0000, 0x00fc0000, 0x00fc0000, 0x00fc0000 } }, + { 6, 0x989c, + { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, + { 6, 0x989c, + { 0x043f0000, 0x043f0000, 0x043f0000, 0x043f0000, 0x043f0000 } }, + { 6, 0x989c, + { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } }, + { 6, 0x989c, + { 0x02190000, 0x02190000, 0x02190000, 0x02190000, 0x02190000 } }, + { 6, 0x989c, + { 0x00240000, 0x00240000, 0x00240000, 0x00240000, 0x00240000 } }, + { 6, 0x989c, + { 0x00b40000, 0x00b40000, 0x00b40000, 0x00b40000, 0x00b40000 } }, + { 6, 0x989c, + { 0x00990000, 0x00990000, 0x00990000, 0x00990000, 0x00990000 } }, + { 6, 0x989c, + { 0x00500000, 0x00500000, 0x00500000, 0x00500000, 0x00500000 } }, + { 6, 0x989c, + { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } }, + { 6, 0x989c, + { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } }, + { 6, 0x989c, + { 0xc0320000, 0xc0320000, 0xc0320000, 0xc0320000, 0xc0320000 } }, + { 6, 0x989c, + { 0x01740000, 0x01740000, 0x01740000, 0x01740000, 0x01740000 } }, + { 6, 0x989c, + { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } }, + { 6, 0x989c, + { 0x86280000, 0x86280000, 0x86280000, 0x86280000, 0x86280000 } }, + { 6, 0x989c, + { 0x31840000, 0x31840000, 0x31840000, 0x31840000, 0x31840000 } }, + { 6, 0x989c, + { 0x00f20080, 0x00f20080, 0x00f20080, 0x00f20080, 0x00f20080 } }, + { 6, 0x989c, + { 0x00270019, 0x00270019, 0x00270019, 0x00270019, 0x00270019 } }, + { 6, 0x989c, + { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x000000b2, 0x000000b2, 0x000000b2, 0x000000b2, 0x000000b2 } }, + { 6, 0x989c, + { 0x00b02084, 0x00b02084, 0x00b02084, 0x00b02084, 0x00b02084 } }, + { 6, 0x989c, + { 0x004125a4, 0x004125a4, 0x004125a4, 0x004125a4, 0x004125a4 } }, + { 6, 0x989c, + { 0x00119220, 0x00119220, 0x00119220, 0x00119220, 0x00119220 } }, + { 6, 0x989c, + { 0x001a4800, 0x001a4800, 0x001a4800, 0x001a4800, 0x001a4800 } }, + { 6, 0x98d8, + { 0x000b0230, 0x000b0230, 0x000b0230, 0x000b0230, 0x000b0230 } }, + { 7, 0x989c, + { 0x00000094, 0x00000094, 0x00000094, 0x00000094, 0x00000094 } }, + { 7, 0x989c, + { 0x00000091, 0x00000091, 0x00000091, 0x00000091, 0x00000091 } }, + { 7, 0x989c, + { 0x00000012, 0x00000012, 0x00000012, 0x00000012, 0x00000012 } }, + { 7, 0x989c, + { 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } }, + { 7, 0x989c, + { 0x000000d9, 0x000000d9, 0x000000d9, 0x000000d9, 0x000000d9 } }, + { 7, 0x989c, + { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } }, + { 7, 0x989c, + { 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0 } }, + { 7, 0x989c, + { 0x000000a2, 0x000000a2, 0x000000a2, 0x000000a2, 0x000000a2 } }, + { 7, 0x989c, + { 0x00000052, 0x00000052, 0x00000052, 0x00000052, 0x00000052 } }, + { 7, 0x989c, + { 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4 } }, + { 7, 0x989c, + { 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc } }, + { 7, 0x989c, + { 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c } }, + { 7, 0x98c4, + { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } }, +}; + + + +/******************\ +* RF2413 (Griffin) * +\******************/ + +/* BANK 6 len pos col */ +#define AR5K_RF2413_OB_2GHZ { 3, 168, 0 } +#define AR5K_RF2413_DB_2GHZ { 3, 165, 0 } + +static const struct ath5k_rf_reg rf_regs_2413[] = { + {6, AR5K_RF_OB_2GHZ, AR5K_RF2413_OB_2GHZ}, + {6, AR5K_RF_DB_2GHZ, AR5K_RF2413_DB_2GHZ}, +}; + +/* Default mode specific settings + * XXX: a/aTurbo ??? + */ +static const struct ath5k_ini_rfbuffer rfb_2413[] = { + { 1, 0x98d4, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d0, + { 0x02001408, 0x02011408, 0x02001408, 0x02001408, 0x02011408 } }, + { 3, 0x98dc, + { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } }, + { 6, 0x989c, + { 0xf0000000, 0xf0000000, 0xf0000000, 0xf0000000, 0xf0000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x40400000, 0x40400000, 0x40400000, 0x40400000, 0x40400000 } }, + { 6, 0x989c, + { 0x65050000, 0x65050000, 0x65050000, 0x65050000, 0x65050000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00420000, 0x00420000, 0x00420000, 0x00420000, 0x00420000 } }, + { 6, 0x989c, + { 0x00b50000, 0x00b50000, 0x00b50000, 0x00b50000, 0x00b50000 } }, + { 6, 0x989c, + { 0x00030000, 0x00030000, 0x00030000, 0x00030000, 0x00030000 } }, + { 6, 0x989c, + { 0x00f70000, 0x00f70000, 0x00f70000, 0x00f70000, 0x00f70000 } }, + { 6, 0x989c, + { 0x009d0000, 0x009d0000, 0x009d0000, 0x009d0000, 0x009d0000 } }, + { 6, 0x989c, + { 0x00220000, 0x00220000, 0x00220000, 0x00220000, 0x00220000 } }, + { 6, 0x989c, + { 0x04220000, 0x04220000, 0x04220000, 0x04220000, 0x04220000 } }, + { 6, 0x989c, + { 0x00230018, 0x00230018, 0x00230018, 0x00230018, 0x00230018 } }, + { 6, 0x989c, + { 0x00280000, 0x00280000, 0x00280060, 0x00280060, 0x00280060 } }, + { 6, 0x989c, + { 0x005000c0, 0x005000c0, 0x005000c3, 0x005000c3, 0x005000c3 } }, + { 6, 0x989c, + { 0x0004007f, 0x0004007f, 0x0004007f, 0x0004007f, 0x0004007f } }, + { 6, 0x989c, + { 0x00000458, 0x00000458, 0x00000458, 0x00000458, 0x00000458 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x0000c000, 0x0000c000, 0x0000c000, 0x0000c000, 0x0000c000 } }, + { 6, 0x98d8, + { 0x00400230, 0x00400230, 0x00400230, 0x00400230, 0x00400230 } }, + { 7, 0x989c, + { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } }, + { 7, 0x989c, + { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } }, + { 7, 0x98cc, + { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } }, +}; + + + +/***************************\ +* RF2315/RF2316 (Cobra SoC) * +\***************************/ + +/* BANK 6 len pos col */ +#define AR5K_RF2316_OB_2GHZ { 3, 178, 0 } +#define AR5K_RF2316_DB_2GHZ { 3, 175, 0 } + +static const struct ath5k_rf_reg rf_regs_2316[] = { + {6, AR5K_RF_OB_2GHZ, AR5K_RF2316_OB_2GHZ}, + {6, AR5K_RF_DB_2GHZ, AR5K_RF2316_DB_2GHZ}, +}; + +/* Default mode specific settings */ +static const struct ath5k_ini_rfbuffer rfb_2316[] = { + { 1, 0x98d4, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d0, + { 0x02001408, 0x02011408, 0x02001408, 0x02001408, 0x02011408 } }, + { 3, 0x98dc, + { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000 } }, + { 6, 0x989c, + { 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000 } }, + { 6, 0x989c, + { 0x02000000, 0x02000000, 0x02000000, 0x02000000, 0x02000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0xf8000000, 0xf8000000, 0xf8000000, 0xf8000000, 0xf8000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x95150000, 0x95150000, 0x95150000, 0x95150000, 0x95150000 } }, + { 6, 0x989c, + { 0xc1000000, 0xc1000000, 0xc1000000, 0xc1000000, 0xc1000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000 } }, + { 6, 0x989c, + { 0x00d50000, 0x00d50000, 0x00d50000, 0x00d50000, 0x00d50000 } }, + { 6, 0x989c, + { 0x000e0000, 0x000e0000, 0x000e0000, 0x000e0000, 0x000e0000 } }, + { 6, 0x989c, + { 0x00dc0000, 0x00dc0000, 0x00dc0000, 0x00dc0000, 0x00dc0000 } }, + { 6, 0x989c, + { 0x00770000, 0x00770000, 0x00770000, 0x00770000, 0x00770000 } }, + { 6, 0x989c, + { 0x008a0000, 0x008a0000, 0x008a0000, 0x008a0000, 0x008a0000 } }, + { 6, 0x989c, + { 0x10880000, 0x10880000, 0x10880000, 0x10880000, 0x10880000 } }, + { 6, 0x989c, + { 0x008c0060, 0x008c0060, 0x008c0060, 0x008c0060, 0x008c0060 } }, + { 6, 0x989c, + { 0x00a00000, 0x00a00000, 0x00a00080, 0x00a00080, 0x00a00080 } }, + { 6, 0x989c, + { 0x00400000, 0x00400000, 0x0040000d, 0x0040000d, 0x0040000d } }, + { 6, 0x989c, + { 0x00110400, 0x00110400, 0x00110400, 0x00110400, 0x00110400 } }, + { 6, 0x989c, + { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } }, + { 6, 0x989c, + { 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001 } }, + { 6, 0x989c, + { 0x00000b00, 0x00000b00, 0x00000b00, 0x00000b00, 0x00000b00 } }, + { 6, 0x989c, + { 0x00000be8, 0x00000be8, 0x00000be8, 0x00000be8, 0x00000be8 } }, + { 6, 0x98c0, + { 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000 } }, + { 7, 0x989c, + { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } }, + { 7, 0x989c, + { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } }, + { 7, 0x98cc, + { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } }, +}; + + + +/******************************\ +* RF5413/RF5424 (Eagle/Condor) * +\******************************/ + +/* BANK 6 len pos col */ +#define AR5K_RF5413_OB_2GHZ { 3, 241, 0 } +#define AR5K_RF5413_DB_2GHZ { 3, 238, 0 } + +#define AR5K_RF5413_OB_5GHZ { 3, 247, 0 } +#define AR5K_RF5413_DB_5GHZ { 3, 244, 0 } + +#define AR5K_RF5413_PWD_ICLOBUF2G { 3, 131, 3 } +#define AR5K_RF5413_DERBY_CHAN_SEL_MODE { 1, 291, 2 } + +static const struct ath5k_rf_reg rf_regs_5413[] = { + {6, AR5K_RF_OB_2GHZ, AR5K_RF5413_OB_2GHZ}, + {6, AR5K_RF_DB_2GHZ, AR5K_RF5413_DB_2GHZ}, + {6, AR5K_RF_OB_5GHZ, AR5K_RF5413_OB_5GHZ}, + {6, AR5K_RF_DB_5GHZ, AR5K_RF5413_DB_5GHZ}, + {6, AR5K_RF_PWD_ICLOBUF_2G, AR5K_RF5413_PWD_ICLOBUF2G}, + {6, AR5K_RF_DERBY_CHAN_SEL_MODE, AR5K_RF5413_DERBY_CHAN_SEL_MODE}, +}; + +/* Default mode specific settings */ +static const struct ath5k_ini_rfbuffer rfb_5413[] = { + { 1, 0x98d4, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d0, + { 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008 } }, + { 3, 0x98dc, + { 0x00a000c0, 0x00a000c0, 0x00e000c0, 0x00e000c0, 0x00e000c0 } }, + { 6, 0x989c, + { 0x33000000, 0x33000000, 0x33000000, 0x33000000, 0x33000000 } }, + { 6, 0x989c, + { 0x01000000, 0x01000000, 0x01000000, 0x01000000, 0x01000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x1f000000, 0x1f000000, 0x1f000000, 0x1f000000, 0x1f000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00b80000, 0x00b80000, 0x00b80000, 0x00b80000, 0x00b80000 } }, + { 6, 0x989c, + { 0x00b70000, 0x00b70000, 0x00b70000, 0x00b70000, 0x00b70000 } }, + { 6, 0x989c, + { 0x00840000, 0x00840000, 0x00840000, 0x00840000, 0x00840000 } }, + { 6, 0x989c, + { 0x00980000, 0x00980000, 0x00980000, 0x00980000, 0x00980000 } }, + { 6, 0x989c, + { 0x00c00000, 0x00c00000, 0x00c00000, 0x00c00000, 0x00c00000 } }, + { 6, 0x989c, + { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, + { 6, 0x989c, + { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, + { 6, 0x989c, + { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, + { 6, 0x989c, + { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, + { 6, 0x989c, + { 0x00d70000, 0x00d70000, 0x00d70000, 0x00d70000, 0x00d70000 } }, + { 6, 0x989c, + { 0x00610000, 0x00610000, 0x00610000, 0x00610000, 0x00610000 } }, + { 6, 0x989c, + { 0x00fe0000, 0x00fe0000, 0x00fe0000, 0x00fe0000, 0x00fe0000 } }, + { 6, 0x989c, + { 0x00de0000, 0x00de0000, 0x00de0000, 0x00de0000, 0x00de0000 } }, + { 6, 0x989c, + { 0x007f0000, 0x007f0000, 0x007f0000, 0x007f0000, 0x007f0000 } }, + { 6, 0x989c, + { 0x043d0000, 0x043d0000, 0x043d0000, 0x043d0000, 0x043d0000 } }, + { 6, 0x989c, + { 0x00770000, 0x00770000, 0x00770000, 0x00770000, 0x00770000 } }, + { 6, 0x989c, + { 0x00440000, 0x00440000, 0x00440000, 0x00440000, 0x00440000 } }, + { 6, 0x989c, + { 0x00980000, 0x00980000, 0x00980000, 0x00980000, 0x00980000 } }, + { 6, 0x989c, + { 0x00100080, 0x00100080, 0x00100080, 0x00100080, 0x00100080 } }, + { 6, 0x989c, + { 0x0005c034, 0x0005c034, 0x0005c034, 0x0005c034, 0x0005c034 } }, + { 6, 0x989c, + { 0x003100f0, 0x003100f0, 0x003100f0, 0x003100f0, 0x003100f0 } }, + { 6, 0x989c, + { 0x000c011f, 0x000c011f, 0x000c011f, 0x000c011f, 0x000c011f } }, + { 6, 0x989c, + { 0x00510040, 0x00510040, 0x00510040, 0x00510040, 0x00510040 } }, + { 6, 0x989c, + { 0x005000da, 0x005000da, 0x005000da, 0x005000da, 0x005000da } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00004044, 0x00004044, 0x00004044, 0x00004044, 0x00004044 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x000060c0, 0x000060c0, 0x000060c0, 0x000060c0, 0x000060c0 } }, + { 6, 0x989c, + { 0x00002c00, 0x00002c00, 0x00003600, 0x00003600, 0x00002c00 } }, + { 6, 0x98c8, + { 0x00000403, 0x00000403, 0x00040403, 0x00040403, 0x00040403 } }, + { 7, 0x989c, + { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } }, + { 7, 0x989c, + { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } }, + { 7, 0x98cc, + { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } }, +}; + + + +/***************************\ +* RF2425/RF2417 (Swan/Nala) * +* AR2317 (Spider SoC) * +\***************************/ + +/* BANK 6 len pos col */ +#define AR5K_RF2425_OB_2GHZ { 3, 193, 0 } +#define AR5K_RF2425_DB_2GHZ { 3, 190, 0 } + +static const struct ath5k_rf_reg rf_regs_2425[] = { + {6, AR5K_RF_OB_2GHZ, AR5K_RF2425_OB_2GHZ}, + {6, AR5K_RF_DB_2GHZ, AR5K_RF2425_DB_2GHZ}, +}; + +/* Default mode specific settings + * XXX: a/aTurbo ? + */ +static const struct ath5k_ini_rfbuffer rfb_2425[] = { + { 1, 0x98d4, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d0, + { 0x02001408, 0x02001408, 0x02001408, 0x02001408, 0x02001408 } }, + { 3, 0x98dc, + { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } }, + { 6, 0x989c, + { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000 } }, + { 6, 0x989c, + { 0x00020000, 0x00020000, 0x00020000, 0x00020000, 0x00020000 } }, + { 6, 0x989c, + { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } }, + { 6, 0x989c, + { 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000 } }, + { 6, 0x989c, + { 0x00e70000, 0x00e70000, 0x00e70000, 0x00e70000, 0x00e70000 } }, + { 6, 0x989c, + { 0x00140000, 0x00140000, 0x00140000, 0x00140000, 0x00140000 } }, + { 6, 0x989c, + { 0x00910040, 0x00910040, 0x00910040, 0x00910040, 0x00910040 } }, + { 6, 0x989c, + { 0x0007001a, 0x0007001a, 0x0007001a, 0x0007001a, 0x0007001a } }, + { 6, 0x989c, + { 0x00410000, 0x00410000, 0x00410000, 0x00410000, 0x00410000 } }, + { 6, 0x989c, + { 0x00810000, 0x00810000, 0x00810060, 0x00810060, 0x00810060 } }, + { 6, 0x989c, + { 0x00020800, 0x00020800, 0x00020803, 0x00020803, 0x00020803 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00001660, 0x00001660, 0x00001660, 0x00001660, 0x00001660 } }, + { 6, 0x989c, + { 0x00001688, 0x00001688, 0x00001688, 0x00001688, 0x00001688 } }, + { 6, 0x98c4, + { 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001 } }, + { 7, 0x989c, + { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } }, + { 7, 0x989c, + { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } }, + { 7, 0x98cc, + { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } }, +}; + +/* + * TODO: Handle the few differences with swan during + * bank modification and get rid of this + */ +static const struct ath5k_ini_rfbuffer rfb_2317[] = { + { 1, 0x98d4, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d0, + { 0x02001408, 0x02011408, 0x02001408, 0x02001408, 0x02011408 } }, + { 3, 0x98dc, + { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } }, + { 6, 0x989c, + { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000 } }, + { 6, 0x989c, + { 0x00020000, 0x00020000, 0x00020000, 0x00020000, 0x00020000 } }, + { 6, 0x989c, + { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } }, + { 6, 0x989c, + { 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000 } }, + { 6, 0x989c, + { 0x00e70000, 0x00e70000, 0x00e70000, 0x00e70000, 0x00e70000 } }, + { 6, 0x989c, + { 0x00140100, 0x00140100, 0x00140100, 0x00140100, 0x00140100 } }, + { 6, 0x989c, + { 0x00910040, 0x00910040, 0x00910040, 0x00910040, 0x00910040 } }, + { 6, 0x989c, + { 0x0007001a, 0x0007001a, 0x0007001a, 0x0007001a, 0x0007001a } }, + { 6, 0x989c, + { 0x00410000, 0x00410000, 0x00410000, 0x00410000, 0x00410000 } }, + { 6, 0x989c, + { 0x00810000, 0x00810000, 0x00810060, 0x00810060, 0x00810060 } }, + { 6, 0x989c, + { 0x00020800, 0x00020800, 0x00020803, 0x00020803, 0x00020803 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00001660, 0x00001660, 0x00001660, 0x00001660, 0x00001660 } }, + { 6, 0x989c, + { 0x00009688, 0x00009688, 0x00009688, 0x00009688, 0x00009688 } }, + { 6, 0x98c4, + { 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001 } }, + { 7, 0x989c, + { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } }, + { 7, 0x989c, + { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } }, + { 7, 0x98cc, + { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } }, +}; + +/* + * TODO: Handle the few differences with swan during + * bank modification and get rid of this + * XXX: a/aTurbo ? + */ +static const struct ath5k_ini_rfbuffer rfb_2417[] = { + { 1, 0x98d4, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d0, + { 0x02001408, 0x02001408, 0x02001408, 0x02001408, 0x02001408 } }, + { 3, 0x98dc, + { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } }, + { 6, 0x989c, + { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000 } }, + { 6, 0x989c, + { 0x00020000, 0x00020000, 0x00020000, 0x00020000, 0x00020000 } }, + { 6, 0x989c, + { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } }, + { 6, 0x989c, + { 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000 } }, + { 6, 0x989c, + { 0x00e70000, 0x00e70000, 0x80e70000, 0x80e70000, 0x00e70000 } }, + { 6, 0x989c, + { 0x00140000, 0x00140000, 0x00140000, 0x00140000, 0x00140000 } }, + { 6, 0x989c, + { 0x00910040, 0x00910040, 0x00910040, 0x00910040, 0x00910040 } }, + { 6, 0x989c, + { 0x0007001a, 0x0007001a, 0x0207001a, 0x0207001a, 0x0007001a } }, + { 6, 0x989c, + { 0x00410000, 0x00410000, 0x00410000, 0x00410000, 0x00410000 } }, + { 6, 0x989c, + { 0x00810000, 0x00810000, 0x00810060, 0x00810060, 0x00810060 } }, + { 6, 0x989c, + { 0x00020800, 0x00020800, 0x00020803, 0x00020803, 0x00020803 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00001660, 0x00001660, 0x00001660, 0x00001660, 0x00001660 } }, + { 6, 0x989c, + { 0x00001688, 0x00001688, 0x00001688, 0x00001688, 0x00001688 } }, + { 6, 0x98c4, + { 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001 } }, + { 7, 0x989c, + { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } }, + { 7, 0x989c, + { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } }, + { 7, 0x98cc, + { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } }, +}; diff --git a/drivers/net/wireless/ath/ath5k/rfgain.h b/drivers/net/wireless/ath/ath5k/rfgain.h new file mode 100644 index 00000000000..1354d8c392c --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/rfgain.h @@ -0,0 +1,516 @@ +/* + * RF Gain optimization + * + * Copyright (c) 2004-2009 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com> + * + * Permission to use, copy, modify, and 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. + * + */ + +/* + * Mode-specific RF Gain table (64bytes) for RF5111/5112 + * (RF5110 only comes with AR5210 and only supports a/turbo a mode so initial + * RF Gain values are included in AR5K_AR5210_INI) + */ +struct ath5k_ini_rfgain { + u16 rfg_register; /* RF Gain register address */ + u32 rfg_value[2]; /* [freq (see below)] */ +}; + +/* Initial RF Gain settings for RF5111 */ +static const struct ath5k_ini_rfgain rfgain_5111[] = { + /* 5Ghz 2Ghz */ + { AR5K_RF_GAIN(0), { 0x000001a9, 0x00000000 } }, + { AR5K_RF_GAIN(1), { 0x000001e9, 0x00000040 } }, + { AR5K_RF_GAIN(2), { 0x00000029, 0x00000080 } }, + { AR5K_RF_GAIN(3), { 0x00000069, 0x00000150 } }, + { AR5K_RF_GAIN(4), { 0x00000199, 0x00000190 } }, + { AR5K_RF_GAIN(5), { 0x000001d9, 0x000001d0 } }, + { AR5K_RF_GAIN(6), { 0x00000019, 0x00000010 } }, + { AR5K_RF_GAIN(7), { 0x00000059, 0x00000044 } }, + { AR5K_RF_GAIN(8), { 0x00000099, 0x00000084 } }, + { AR5K_RF_GAIN(9), { 0x000001a5, 0x00000148 } }, + { AR5K_RF_GAIN(10), { 0x000001e5, 0x00000188 } }, + { AR5K_RF_GAIN(11), { 0x00000025, 0x000001c8 } }, + { AR5K_RF_GAIN(12), { 0x000001c8, 0x00000014 } }, + { AR5K_RF_GAIN(13), { 0x00000008, 0x00000042 } }, + { AR5K_RF_GAIN(14), { 0x00000048, 0x00000082 } }, + { AR5K_RF_GAIN(15), { 0x00000088, 0x00000178 } }, + { AR5K_RF_GAIN(16), { 0x00000198, 0x000001b8 } }, + { AR5K_RF_GAIN(17), { 0x000001d8, 0x000001f8 } }, + { AR5K_RF_GAIN(18), { 0x00000018, 0x00000012 } }, + { AR5K_RF_GAIN(19), { 0x00000058, 0x00000052 } }, + { AR5K_RF_GAIN(20), { 0x00000098, 0x00000092 } }, + { AR5K_RF_GAIN(21), { 0x000001a4, 0x0000017c } }, + { AR5K_RF_GAIN(22), { 0x000001e4, 0x000001bc } }, + { AR5K_RF_GAIN(23), { 0x00000024, 0x000001fc } }, + { AR5K_RF_GAIN(24), { 0x00000064, 0x0000000a } }, + { AR5K_RF_GAIN(25), { 0x000000a4, 0x0000004a } }, + { AR5K_RF_GAIN(26), { 0x000000e4, 0x0000008a } }, + { AR5K_RF_GAIN(27), { 0x0000010a, 0x0000015a } }, + { AR5K_RF_GAIN(28), { 0x0000014a, 0x0000019a } }, + { AR5K_RF_GAIN(29), { 0x0000018a, 0x000001da } }, + { AR5K_RF_GAIN(30), { 0x000001ca, 0x0000000e } }, + { AR5K_RF_GAIN(31), { 0x0000000a, 0x0000004e } }, + { AR5K_RF_GAIN(32), { 0x0000004a, 0x0000008e } }, + { AR5K_RF_GAIN(33), { 0x0000008a, 0x0000015e } }, + { AR5K_RF_GAIN(34), { 0x000001ba, 0x0000019e } }, + { AR5K_RF_GAIN(35), { 0x000001fa, 0x000001de } }, + { AR5K_RF_GAIN(36), { 0x0000003a, 0x00000009 } }, + { AR5K_RF_GAIN(37), { 0x0000007a, 0x00000049 } }, + { AR5K_RF_GAIN(38), { 0x00000186, 0x00000089 } }, + { AR5K_RF_GAIN(39), { 0x000001c6, 0x00000179 } }, + { AR5K_RF_GAIN(40), { 0x00000006, 0x000001b9 } }, + { AR5K_RF_GAIN(41), { 0x00000046, 0x000001f9 } }, + { AR5K_RF_GAIN(42), { 0x00000086, 0x00000039 } }, + { AR5K_RF_GAIN(43), { 0x000000c6, 0x00000079 } }, + { AR5K_RF_GAIN(44), { 0x000000c6, 0x000000b9 } }, + { AR5K_RF_GAIN(45), { 0x000000c6, 0x000001bd } }, + { AR5K_RF_GAIN(46), { 0x000000c6, 0x000001fd } }, + { AR5K_RF_GAIN(47), { 0x000000c6, 0x0000003d } }, + { AR5K_RF_GAIN(48), { 0x000000c6, 0x0000007d } }, + { AR5K_RF_GAIN(49), { 0x000000c6, 0x000000bd } }, + { AR5K_RF_GAIN(50), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(51), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(52), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(53), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(54), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(55), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(56), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(57), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(58), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(59), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(60), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(61), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(62), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(63), { 0x000000c6, 0x000000fd } }, +}; + +/* Initial RF Gain settings for RF5112 */ +static const struct ath5k_ini_rfgain rfgain_5112[] = { + /* 5Ghz 2Ghz */ + { AR5K_RF_GAIN(0), { 0x00000007, 0x00000007 } }, + { AR5K_RF_GAIN(1), { 0x00000047, 0x00000047 } }, + { AR5K_RF_GAIN(2), { 0x00000087, 0x00000087 } }, + { AR5K_RF_GAIN(3), { 0x000001a0, 0x000001a0 } }, + { AR5K_RF_GAIN(4), { 0x000001e0, 0x000001e0 } }, + { AR5K_RF_GAIN(5), { 0x00000020, 0x00000020 } }, + { AR5K_RF_GAIN(6), { 0x00000060, 0x00000060 } }, + { AR5K_RF_GAIN(7), { 0x000001a1, 0x000001a1 } }, + { AR5K_RF_GAIN(8), { 0x000001e1, 0x000001e1 } }, + { AR5K_RF_GAIN(9), { 0x00000021, 0x00000021 } }, + { AR5K_RF_GAIN(10), { 0x00000061, 0x00000061 } }, + { AR5K_RF_GAIN(11), { 0x00000162, 0x00000162 } }, + { AR5K_RF_GAIN(12), { 0x000001a2, 0x000001a2 } }, + { AR5K_RF_GAIN(13), { 0x000001e2, 0x000001e2 } }, + { AR5K_RF_GAIN(14), { 0x00000022, 0x00000022 } }, + { AR5K_RF_GAIN(15), { 0x00000062, 0x00000062 } }, + { AR5K_RF_GAIN(16), { 0x00000163, 0x00000163 } }, + { AR5K_RF_GAIN(17), { 0x000001a3, 0x000001a3 } }, + { AR5K_RF_GAIN(18), { 0x000001e3, 0x000001e3 } }, + { AR5K_RF_GAIN(19), { 0x00000023, 0x00000023 } }, + { AR5K_RF_GAIN(20), { 0x00000063, 0x00000063 } }, + { AR5K_RF_GAIN(21), { 0x00000184, 0x00000184 } }, + { AR5K_RF_GAIN(22), { 0x000001c4, 0x000001c4 } }, + { AR5K_RF_GAIN(23), { 0x00000004, 0x00000004 } }, + { AR5K_RF_GAIN(24), { 0x000001ea, 0x0000000b } }, + { AR5K_RF_GAIN(25), { 0x0000002a, 0x0000004b } }, + { AR5K_RF_GAIN(26), { 0x0000006a, 0x0000008b } }, + { AR5K_RF_GAIN(27), { 0x000000aa, 0x000001ac } }, + { AR5K_RF_GAIN(28), { 0x000001ab, 0x000001ec } }, + { AR5K_RF_GAIN(29), { 0x000001eb, 0x0000002c } }, + { AR5K_RF_GAIN(30), { 0x0000002b, 0x00000012 } }, + { AR5K_RF_GAIN(31), { 0x0000006b, 0x00000052 } }, + { AR5K_RF_GAIN(32), { 0x000000ab, 0x00000092 } }, + { AR5K_RF_GAIN(33), { 0x000001ac, 0x00000193 } }, + { AR5K_RF_GAIN(34), { 0x000001ec, 0x000001d3 } }, + { AR5K_RF_GAIN(35), { 0x0000002c, 0x00000013 } }, + { AR5K_RF_GAIN(36), { 0x0000003a, 0x00000053 } }, + { AR5K_RF_GAIN(37), { 0x0000007a, 0x00000093 } }, + { AR5K_RF_GAIN(38), { 0x000000ba, 0x00000194 } }, + { AR5K_RF_GAIN(39), { 0x000001bb, 0x000001d4 } }, + { AR5K_RF_GAIN(40), { 0x000001fb, 0x00000014 } }, + { AR5K_RF_GAIN(41), { 0x0000003b, 0x0000003a } }, + { AR5K_RF_GAIN(42), { 0x0000007b, 0x0000007a } }, + { AR5K_RF_GAIN(43), { 0x000000bb, 0x000000ba } }, + { AR5K_RF_GAIN(44), { 0x000001bc, 0x000001bb } }, + { AR5K_RF_GAIN(45), { 0x000001fc, 0x000001fb } }, + { AR5K_RF_GAIN(46), { 0x0000003c, 0x0000003b } }, + { AR5K_RF_GAIN(47), { 0x0000007c, 0x0000007b } }, + { AR5K_RF_GAIN(48), { 0x000000bc, 0x000000bb } }, + { AR5K_RF_GAIN(49), { 0x000000fc, 0x000001bc } }, + { AR5K_RF_GAIN(50), { 0x000000fc, 0x000001fc } }, + { AR5K_RF_GAIN(51), { 0x000000fc, 0x0000003c } }, + { AR5K_RF_GAIN(52), { 0x000000fc, 0x0000007c } }, + { AR5K_RF_GAIN(53), { 0x000000fc, 0x000000bc } }, + { AR5K_RF_GAIN(54), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(55), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(56), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(57), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(58), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(59), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(60), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(61), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(62), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(63), { 0x000000fc, 0x000000fc } }, +}; + +/* Initial RF Gain settings for RF2413 */ +static const struct ath5k_ini_rfgain rfgain_2413[] = { + { AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } }, + { AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } }, + { AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } }, + { AR5K_RF_GAIN(3), { 0x00000000, 0x00000181 } }, + { AR5K_RF_GAIN(4), { 0x00000000, 0x000001c1 } }, + { AR5K_RF_GAIN(5), { 0x00000000, 0x00000001 } }, + { AR5K_RF_GAIN(6), { 0x00000000, 0x00000041 } }, + { AR5K_RF_GAIN(7), { 0x00000000, 0x00000081 } }, + { AR5K_RF_GAIN(8), { 0x00000000, 0x00000168 } }, + { AR5K_RF_GAIN(9), { 0x00000000, 0x000001a8 } }, + { AR5K_RF_GAIN(10), { 0x00000000, 0x000001e8 } }, + { AR5K_RF_GAIN(11), { 0x00000000, 0x00000028 } }, + { AR5K_RF_GAIN(12), { 0x00000000, 0x00000068 } }, + { AR5K_RF_GAIN(13), { 0x00000000, 0x00000189 } }, + { AR5K_RF_GAIN(14), { 0x00000000, 0x000001c9 } }, + { AR5K_RF_GAIN(15), { 0x00000000, 0x00000009 } }, + { AR5K_RF_GAIN(16), { 0x00000000, 0x00000049 } }, + { AR5K_RF_GAIN(17), { 0x00000000, 0x00000089 } }, + { AR5K_RF_GAIN(18), { 0x00000000, 0x00000190 } }, + { AR5K_RF_GAIN(19), { 0x00000000, 0x000001d0 } }, + { AR5K_RF_GAIN(20), { 0x00000000, 0x00000010 } }, + { AR5K_RF_GAIN(21), { 0x00000000, 0x00000050 } }, + { AR5K_RF_GAIN(22), { 0x00000000, 0x00000090 } }, + { AR5K_RF_GAIN(23), { 0x00000000, 0x00000191 } }, + { AR5K_RF_GAIN(24), { 0x00000000, 0x000001d1 } }, + { AR5K_RF_GAIN(25), { 0x00000000, 0x00000011 } }, + { AR5K_RF_GAIN(26), { 0x00000000, 0x00000051 } }, + { AR5K_RF_GAIN(27), { 0x00000000, 0x00000091 } }, + { AR5K_RF_GAIN(28), { 0x00000000, 0x00000178 } }, + { AR5K_RF_GAIN(29), { 0x00000000, 0x000001b8 } }, + { AR5K_RF_GAIN(30), { 0x00000000, 0x000001f8 } }, + { AR5K_RF_GAIN(31), { 0x00000000, 0x00000038 } }, + { AR5K_RF_GAIN(32), { 0x00000000, 0x00000078 } }, + { AR5K_RF_GAIN(33), { 0x00000000, 0x00000199 } }, + { AR5K_RF_GAIN(34), { 0x00000000, 0x000001d9 } }, + { AR5K_RF_GAIN(35), { 0x00000000, 0x00000019 } }, + { AR5K_RF_GAIN(36), { 0x00000000, 0x00000059 } }, + { AR5K_RF_GAIN(37), { 0x00000000, 0x00000099 } }, + { AR5K_RF_GAIN(38), { 0x00000000, 0x000000d9 } }, + { AR5K_RF_GAIN(39), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(40), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(41), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(42), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(43), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(44), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(45), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(46), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(47), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(48), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(49), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(50), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(51), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(52), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(53), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(54), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(55), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(56), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(57), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(58), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(59), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(60), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(61), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(62), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(63), { 0x00000000, 0x000000f9 } }, +}; + +/* Initial RF Gain settings for AR2316 */ +static const struct ath5k_ini_rfgain rfgain_2316[] = { + { AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } }, + { AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } }, + { AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } }, + { AR5K_RF_GAIN(3), { 0x00000000, 0x000000c0 } }, + { AR5K_RF_GAIN(4), { 0x00000000, 0x000000e0 } }, + { AR5K_RF_GAIN(5), { 0x00000000, 0x000000e0 } }, + { AR5K_RF_GAIN(6), { 0x00000000, 0x00000128 } }, + { AR5K_RF_GAIN(7), { 0x00000000, 0x00000128 } }, + { AR5K_RF_GAIN(8), { 0x00000000, 0x00000128 } }, + { AR5K_RF_GAIN(9), { 0x00000000, 0x00000168 } }, + { AR5K_RF_GAIN(10), { 0x00000000, 0x000001a8 } }, + { AR5K_RF_GAIN(11), { 0x00000000, 0x000001e8 } }, + { AR5K_RF_GAIN(12), { 0x00000000, 0x00000028 } }, + { AR5K_RF_GAIN(13), { 0x00000000, 0x00000068 } }, + { AR5K_RF_GAIN(14), { 0x00000000, 0x000000a8 } }, + { AR5K_RF_GAIN(15), { 0x00000000, 0x000000e8 } }, + { AR5K_RF_GAIN(16), { 0x00000000, 0x000000e8 } }, + { AR5K_RF_GAIN(17), { 0x00000000, 0x00000130 } }, + { AR5K_RF_GAIN(18), { 0x00000000, 0x00000130 } }, + { AR5K_RF_GAIN(19), { 0x00000000, 0x00000170 } }, + { AR5K_RF_GAIN(20), { 0x00000000, 0x000001b0 } }, + { AR5K_RF_GAIN(21), { 0x00000000, 0x000001f0 } }, + { AR5K_RF_GAIN(22), { 0x00000000, 0x00000030 } }, + { AR5K_RF_GAIN(23), { 0x00000000, 0x00000070 } }, + { AR5K_RF_GAIN(24), { 0x00000000, 0x000000b0 } }, + { AR5K_RF_GAIN(25), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(26), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(27), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(28), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(29), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(30), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(31), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(32), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(33), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(34), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(35), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(36), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(37), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(38), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(39), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(40), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(41), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(42), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(43), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(44), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(45), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(46), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(47), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(48), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(49), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(50), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(51), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(52), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(53), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(54), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(55), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(56), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(57), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(58), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(59), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(60), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(61), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(62), { 0x00000000, 0x000000f0 } }, + { AR5K_RF_GAIN(63), { 0x00000000, 0x000000f0 } }, +}; + + +/* Initial RF Gain settings for RF5413 */ +static const struct ath5k_ini_rfgain rfgain_5413[] = { + /* 5Ghz 2Ghz */ + { AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } }, + { AR5K_RF_GAIN(1), { 0x00000040, 0x00000040 } }, + { AR5K_RF_GAIN(2), { 0x00000080, 0x00000080 } }, + { AR5K_RF_GAIN(3), { 0x000001a1, 0x00000161 } }, + { AR5K_RF_GAIN(4), { 0x000001e1, 0x000001a1 } }, + { AR5K_RF_GAIN(5), { 0x00000021, 0x000001e1 } }, + { AR5K_RF_GAIN(6), { 0x00000061, 0x00000021 } }, + { AR5K_RF_GAIN(7), { 0x00000188, 0x00000061 } }, + { AR5K_RF_GAIN(8), { 0x000001c8, 0x00000188 } }, + { AR5K_RF_GAIN(9), { 0x00000008, 0x000001c8 } }, + { AR5K_RF_GAIN(10), { 0x00000048, 0x00000008 } }, + { AR5K_RF_GAIN(11), { 0x00000088, 0x00000048 } }, + { AR5K_RF_GAIN(12), { 0x000001a9, 0x00000088 } }, + { AR5K_RF_GAIN(13), { 0x000001e9, 0x00000169 } }, + { AR5K_RF_GAIN(14), { 0x00000029, 0x000001a9 } }, + { AR5K_RF_GAIN(15), { 0x00000069, 0x000001e9 } }, + { AR5K_RF_GAIN(16), { 0x000001d0, 0x00000029 } }, + { AR5K_RF_GAIN(17), { 0x00000010, 0x00000069 } }, + { AR5K_RF_GAIN(18), { 0x00000050, 0x00000190 } }, + { AR5K_RF_GAIN(19), { 0x00000090, 0x000001d0 } }, + { AR5K_RF_GAIN(20), { 0x000001b1, 0x00000010 } }, + { AR5K_RF_GAIN(21), { 0x000001f1, 0x00000050 } }, + { AR5K_RF_GAIN(22), { 0x00000031, 0x00000090 } }, + { AR5K_RF_GAIN(23), { 0x00000071, 0x00000171 } }, + { AR5K_RF_GAIN(24), { 0x000001b8, 0x000001b1 } }, + { AR5K_RF_GAIN(25), { 0x000001f8, 0x000001f1 } }, + { AR5K_RF_GAIN(26), { 0x00000038, 0x00000031 } }, + { AR5K_RF_GAIN(27), { 0x00000078, 0x00000071 } }, + { AR5K_RF_GAIN(28), { 0x00000199, 0x00000198 } }, + { AR5K_RF_GAIN(29), { 0x000001d9, 0x000001d8 } }, + { AR5K_RF_GAIN(30), { 0x00000019, 0x00000018 } }, + { AR5K_RF_GAIN(31), { 0x00000059, 0x00000058 } }, + { AR5K_RF_GAIN(32), { 0x00000099, 0x00000098 } }, + { AR5K_RF_GAIN(33), { 0x000000d9, 0x00000179 } }, + { AR5K_RF_GAIN(34), { 0x000000f9, 0x000001b9 } }, + { AR5K_RF_GAIN(35), { 0x000000f9, 0x000001f9 } }, + { AR5K_RF_GAIN(36), { 0x000000f9, 0x00000039 } }, + { AR5K_RF_GAIN(37), { 0x000000f9, 0x00000079 } }, + { AR5K_RF_GAIN(38), { 0x000000f9, 0x000000b9 } }, + { AR5K_RF_GAIN(39), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(40), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(41), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(42), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(43), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(44), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(45), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(46), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(47), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(48), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(49), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(50), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(51), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(52), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(53), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(54), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(55), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(56), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(57), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(58), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(59), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(60), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(61), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(62), { 0x000000f9, 0x000000f9 } }, + { AR5K_RF_GAIN(63), { 0x000000f9, 0x000000f9 } }, +}; + + +/* Initial RF Gain settings for RF2425 */ +static const struct ath5k_ini_rfgain rfgain_2425[] = { + { AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } }, + { AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } }, + { AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } }, + { AR5K_RF_GAIN(3), { 0x00000000, 0x00000181 } }, + { AR5K_RF_GAIN(4), { 0x00000000, 0x000001c1 } }, + { AR5K_RF_GAIN(5), { 0x00000000, 0x00000001 } }, + { AR5K_RF_GAIN(6), { 0x00000000, 0x00000041 } }, + { AR5K_RF_GAIN(7), { 0x00000000, 0x00000081 } }, + { AR5K_RF_GAIN(8), { 0x00000000, 0x00000188 } }, + { AR5K_RF_GAIN(9), { 0x00000000, 0x000001c8 } }, + { AR5K_RF_GAIN(10), { 0x00000000, 0x00000008 } }, + { AR5K_RF_GAIN(11), { 0x00000000, 0x00000048 } }, + { AR5K_RF_GAIN(12), { 0x00000000, 0x00000088 } }, + { AR5K_RF_GAIN(13), { 0x00000000, 0x00000189 } }, + { AR5K_RF_GAIN(14), { 0x00000000, 0x000001c9 } }, + { AR5K_RF_GAIN(15), { 0x00000000, 0x00000009 } }, + { AR5K_RF_GAIN(16), { 0x00000000, 0x00000049 } }, + { AR5K_RF_GAIN(17), { 0x00000000, 0x00000089 } }, + { AR5K_RF_GAIN(18), { 0x00000000, 0x000001b0 } }, + { AR5K_RF_GAIN(19), { 0x00000000, 0x000001f0 } }, + { AR5K_RF_GAIN(20), { 0x00000000, 0x00000030 } }, + { AR5K_RF_GAIN(21), { 0x00000000, 0x00000070 } }, + { AR5K_RF_GAIN(22), { 0x00000000, 0x00000171 } }, + { AR5K_RF_GAIN(23), { 0x00000000, 0x000001b1 } }, + { AR5K_RF_GAIN(24), { 0x00000000, 0x000001f1 } }, + { AR5K_RF_GAIN(25), { 0x00000000, 0x00000031 } }, + { AR5K_RF_GAIN(26), { 0x00000000, 0x00000071 } }, + { AR5K_RF_GAIN(27), { 0x00000000, 0x000001b8 } }, + { AR5K_RF_GAIN(28), { 0x00000000, 0x000001f8 } }, + { AR5K_RF_GAIN(29), { 0x00000000, 0x00000038 } }, + { AR5K_RF_GAIN(30), { 0x00000000, 0x00000078 } }, + { AR5K_RF_GAIN(31), { 0x00000000, 0x000000b8 } }, + { AR5K_RF_GAIN(32), { 0x00000000, 0x000001b9 } }, + { AR5K_RF_GAIN(33), { 0x00000000, 0x000001f9 } }, + { AR5K_RF_GAIN(34), { 0x00000000, 0x00000039 } }, + { AR5K_RF_GAIN(35), { 0x00000000, 0x00000079 } }, + { AR5K_RF_GAIN(36), { 0x00000000, 0x000000b9 } }, + { AR5K_RF_GAIN(37), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(38), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(39), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(40), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(41), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(42), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(43), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(44), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(45), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(46), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(47), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(48), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(49), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(50), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(51), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(52), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(53), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(54), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(55), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(56), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(57), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(58), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(59), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(60), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(61), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(62), { 0x00000000, 0x000000f9 } }, + { AR5K_RF_GAIN(63), { 0x00000000, 0x000000f9 } }, +}; + +#define AR5K_GAIN_CRN_FIX_BITS_5111 4 +#define AR5K_GAIN_CRN_FIX_BITS_5112 7 +#define AR5K_GAIN_CRN_MAX_FIX_BITS AR5K_GAIN_CRN_FIX_BITS_5112 +#define AR5K_GAIN_DYN_ADJUST_HI_MARGIN 15 +#define AR5K_GAIN_DYN_ADJUST_LO_MARGIN 20 +#define AR5K_GAIN_CCK_PROBE_CORR 5 +#define AR5K_GAIN_CCK_OFDM_GAIN_DELTA 15 +#define AR5K_GAIN_STEP_COUNT 10 + +/* Check if our current measurement is inside our + * current variable attenuation window */ +#define AR5K_GAIN_CHECK_ADJUST(_g) \ + ((_g)->g_current <= (_g)->g_low || (_g)->g_current >= (_g)->g_high) + +struct ath5k_gain_opt_step { + s8 gos_param[AR5K_GAIN_CRN_MAX_FIX_BITS]; + s8 gos_gain; +}; + +struct ath5k_gain_opt { + u8 go_default; + u8 go_steps_count; + const struct ath5k_gain_opt_step go_step[AR5K_GAIN_STEP_COUNT]; +}; + +/* + * Parameters on gos_param: + * 1) Tx clip PHY register + * 2) PWD 90 RF register + * 3) PWD 84 RF register + * 4) RFGainSel RF register + */ +static const struct ath5k_gain_opt rfgain_opt_5111 = { + 4, + 9, + { + { { 4, 1, 1, 1 }, 6 }, + { { 4, 0, 1, 1 }, 4 }, + { { 3, 1, 1, 1 }, 3 }, + { { 4, 0, 0, 1 }, 1 }, + { { 4, 1, 1, 0 }, 0 }, + { { 4, 0, 1, 0 }, -2 }, + { { 3, 1, 1, 0 }, -3 }, + { { 4, 0, 0, 0 }, -4 }, + { { 2, 1, 1, 0 }, -6 } + } +}; + +/* + * Parameters on gos_param: + * 1) Mixgain ovr RF register + * 2) PWD 138 RF register + * 3) PWD 137 RF register + * 4) PWD 136 RF register + * 5) PWD 132 RF register + * 6) PWD 131 RF register + * 7) PWD 130 RF register + */ +static const struct ath5k_gain_opt rfgain_opt_5112 = { + 1, + 8, + { + { { 3, 0, 0, 0, 0, 0, 0 }, 6 }, + { { 2, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 1, 0, 0, 0, 0, 0, 0 }, -3 }, + { { 0, 0, 0, 0, 0, 0, 0 }, -6 }, + { { 0, 1, 1, 0, 0, 0, 0 }, -8 }, + { { 0, 1, 1, 0, 1, 1, 0 }, -10 }, + { { 0, 1, 0, 1, 1, 1, 0 }, -13 }, + { { 0, 1, 0, 1, 1, 0, 1 }, -16 }, + } +}; + diff --git a/drivers/net/wireless/ath/ath5k/rfkill.c b/drivers/net/wireless/ath/ath5k/rfkill.c new file mode 100644 index 00000000000..41a877b73fc --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/rfkill.c @@ -0,0 +1,121 @@ +/* + * RFKILL support for ath5k + * + * Copyright (c) 2009 Tobias Doerffel <tobias.doerffel@gmail.com> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include "base.h" + + +static inline void ath5k_rfkill_disable(struct ath5k_softc *sc) +{ + ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "rfkill disable (gpio:%d polarity:%d)\n", + sc->rf_kill.gpio, sc->rf_kill.polarity); + ath5k_hw_set_gpio_output(sc->ah, sc->rf_kill.gpio); + ath5k_hw_set_gpio(sc->ah, sc->rf_kill.gpio, !sc->rf_kill.polarity); +} + + +static inline void ath5k_rfkill_enable(struct ath5k_softc *sc) +{ + ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "rfkill enable (gpio:%d polarity:%d)\n", + sc->rf_kill.gpio, sc->rf_kill.polarity); + ath5k_hw_set_gpio_output(sc->ah, sc->rf_kill.gpio); + ath5k_hw_set_gpio(sc->ah, sc->rf_kill.gpio, sc->rf_kill.polarity); +} + +static inline void ath5k_rfkill_set_intr(struct ath5k_softc *sc, bool enable) +{ + struct ath5k_hw *ah = sc->ah; + u32 curval; + + ath5k_hw_set_gpio_input(ah, sc->rf_kill.gpio); + curval = ath5k_hw_get_gpio(ah, sc->rf_kill.gpio); + ath5k_hw_set_gpio_intr(ah, sc->rf_kill.gpio, enable ? + !!curval : !curval); +} + +static bool +ath5k_is_rfkill_set(struct ath5k_softc *sc) +{ + /* configuring GPIO for input for some reason disables rfkill */ + /*ath5k_hw_set_gpio_input(sc->ah, sc->rf_kill.gpio);*/ + return ath5k_hw_get_gpio(sc->ah, sc->rf_kill.gpio) == + sc->rf_kill.polarity; +} + +static void +ath5k_tasklet_rfkill_toggle(unsigned long data) +{ + struct ath5k_softc *sc = (void *)data; + bool blocked; + + blocked = ath5k_is_rfkill_set(sc); + wiphy_rfkill_set_hw_state(sc->hw->wiphy, blocked); +} + + +void +ath5k_rfkill_hw_start(struct ath5k_hw *ah) +{ + struct ath5k_softc *sc = ah->ah_sc; + + /* read rfkill GPIO configuration from EEPROM header */ + sc->rf_kill.gpio = ah->ah_capabilities.cap_eeprom.ee_rfkill_pin; + sc->rf_kill.polarity = ah->ah_capabilities.cap_eeprom.ee_rfkill_pol; + + tasklet_init(&sc->rf_kill.toggleq, ath5k_tasklet_rfkill_toggle, + (unsigned long)sc); + + ath5k_rfkill_disable(sc); + + /* enable interrupt for rfkill switch */ + if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header)) + ath5k_rfkill_set_intr(sc, true); +} + + +void +ath5k_rfkill_hw_stop(struct ath5k_hw *ah) +{ + struct ath5k_softc *sc = ah->ah_sc; + + /* disable interrupt for rfkill switch */ + if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header)) + ath5k_rfkill_set_intr(sc, false); + + tasklet_kill(&sc->rf_kill.toggleq); + + /* enable RFKILL when stopping HW so Wifi LED is turned off */ + ath5k_rfkill_enable(sc); +} + diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig new file mode 100644 index 00000000000..0ed1ac312aa --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -0,0 +1,24 @@ +config ATH9K + tristate "Atheros 802.11n wireless cards support" + depends on PCI && MAC80211 && WLAN_80211 + depends on RFKILL || RFKILL=n + select ATH_COMMON + select MAC80211_LEDS + select LEDS_CLASS + select NEW_LEDS + ---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. + +config ATH9K_DEBUG + bool "Atheros ath9k debugging" + depends on ATH9K + ---help--- + Say Y, if you need ath9k to display debug messages. + Pass the debug mask as a module parameter: + + modprobe ath9k debug=0x00002000 + + Look in ath9k/core.h for possible debug masks diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile new file mode 100644 index 00000000000..783bc39eb2f --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -0,0 +1,18 @@ +ath9k-y += hw.o \ + eeprom.o \ + mac.o \ + calib.o \ + ani.o \ + phy.o \ + beacon.o \ + main.o \ + recv.o \ + xmit.o \ + virtual.o \ + rc.o + +ath9k-$(CONFIG_PCI) += pci.o +ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o +ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o + +obj-$(CONFIG_ATH9K) += ath9k.o diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c new file mode 100644 index 00000000000..0e65c51ba17 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org> + * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org> + * + * 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 <linux/nl80211.h> +#include <linux/platform_device.h> +#include <linux/ath9k_platform.h> +#include "ath9k.h" + +/* return bus cachesize in 4B word units */ +static void ath_ahb_read_cachesize(struct ath_softc *sc, int *csz) +{ + *csz = L1_CACHE_BYTES >> 2; +} + +static void ath_ahb_cleanup(struct ath_softc *sc) +{ + iounmap(sc->mem); +} + +static bool ath_ahb_eeprom_read(struct ath_hw *ah, u32 off, u16 *data) +{ + struct ath_softc *sc = ah->ah_sc; + struct platform_device *pdev = to_platform_device(sc->dev); + struct ath9k_platform_data *pdata; + + pdata = (struct ath9k_platform_data *) pdev->dev.platform_data; + if (off >= (ARRAY_SIZE(pdata->eeprom_data))) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "%s: flash read failed, offset %08x is out of range\n", + __func__, off); + return false; + } + + *data = pdata->eeprom_data[off]; + return true; +} + +static struct ath_bus_ops ath_ahb_bus_ops = { + .read_cachesize = ath_ahb_read_cachesize, + .cleanup = ath_ahb_cleanup, + + .eeprom_read = ath_ahb_eeprom_read, +}; + +static int ath_ahb_probe(struct platform_device *pdev) +{ + void __iomem *mem; + struct ath_wiphy *aphy; + struct ath_softc *sc; + struct ieee80211_hw *hw; + struct resource *res; + int irq; + int ret = 0; + struct ath_hw *ah; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data specified\n"); + ret = -EINVAL; + goto err_out; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no memory resource found\n"); + ret = -ENXIO; + goto err_out; + } + + mem = ioremap_nocache(res->start, res->end - res->start + 1); + if (mem == NULL) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_out; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no IRQ resource found\n"); + ret = -ENXIO; + goto err_iounmap; + } + + irq = res->start; + + hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy) + + sizeof(struct ath_softc), &ath9k_ops); + if (hw == NULL) { + dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); + ret = -ENOMEM; + goto err_iounmap; + } + + SET_IEEE80211_DEV(hw, &pdev->dev); + platform_set_drvdata(pdev, hw); + + aphy = hw->priv; + sc = (struct ath_softc *) (aphy + 1); + aphy->sc = sc; + aphy->hw = hw; + sc->pri_wiphy = aphy; + sc->hw = hw; + sc->dev = &pdev->dev; + sc->mem = mem; + sc->bus_ops = &ath_ahb_bus_ops; + sc->irq = irq; + + ret = ath_attach(AR5416_AR9100_DEVID, sc); + if (ret != 0) { + dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret); + ret = -ENODEV; + goto err_free_hw; + } + + ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc); + if (ret) { + dev_err(&pdev->dev, "request_irq failed, err=%d\n", ret); + ret = -EIO; + goto err_detach; + } + + ah = sc->sc_ah; + printk(KERN_INFO + "%s: Atheros AR%s MAC/BB Rev:%x, " + "AR%s RF Rev:%x, mem=0x%lx, irq=%d\n", + wiphy_name(hw->wiphy), + ath_mac_bb_name(ah->hw_version.macVersion), + ah->hw_version.macRev, + ath_rf_name((ah->hw_version.analog5GhzRev & AR_RADIO_SREV_MAJOR)), + ah->hw_version.phyRev, + (unsigned long)mem, irq); + + return 0; + + err_detach: + ath_detach(sc); + err_free_hw: + ieee80211_free_hw(hw); + platform_set_drvdata(pdev, NULL); + err_iounmap: + iounmap(mem); + err_out: + return ret; +} + +static int ath_ahb_remove(struct platform_device *pdev) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + + if (hw) { + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + ath_cleanup(sc); + platform_set_drvdata(pdev, NULL); + } + + return 0; +} + +static struct platform_driver ath_ahb_driver = { + .probe = ath_ahb_probe, + .remove = ath_ahb_remove, + .driver = { + .name = "ath9k", + .owner = THIS_MODULE, + }, +}; + +int ath_ahb_init(void) +{ + return platform_driver_register(&ath_ahb_driver); +} + +void ath_ahb_exit(void) +{ + platform_driver_unregister(&ath_ahb_driver); +} diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c new file mode 100644 index 00000000000..1aeafb511dd --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -0,0 +1,822 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +static int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ah->ani); i++) { + if (ah->ani[i].c && + ah->ani[i].c->channel == chan->channel) + return i; + if (ah->ani[i].c == NULL) { + ah->ani[i].c = chan; + return i; + } + } + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "No more channel states left. Using channel 0\n"); + + return 0; +} + +static bool ath9k_hw_ani_control(struct ath_hw *ah, + enum ath9k_ani_cmd cmd, int param) +{ + struct ar5416AniState *aniState = ah->curani; + + switch (cmd & ah->ani_function) { + case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{ + u32 level = param; + + if (level >= ARRAY_SIZE(ah->totalSizeDesired)) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "level out of range (%u > %u)\n", + level, + (unsigned)ARRAY_SIZE(ah->totalSizeDesired)); + return false; + } + + REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, + AR_PHY_DESIRED_SZ_TOT_DES, + ah->totalSizeDesired[level]); + REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, + AR_PHY_AGC_CTL1_COARSE_LOW, + ah->coarse_low[level]); + REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, + AR_PHY_AGC_CTL1_COARSE_HIGH, + ah->coarse_high[level]); + REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, + AR_PHY_FIND_SIG_FIRPWR, + ah->firpwr[level]); + + if (level > aniState->noiseImmunityLevel) + ah->stats.ast_ani_niup++; + else if (level < aniState->noiseImmunityLevel) + 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) + ah->stats.ast_ani_ofdmon++; + else + 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) + ah->stats.ast_ani_cckhigh++; + else + 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, + "level out of range (%u > %u)\n", + 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) + ah->stats.ast_ani_stepup++; + else if (level < aniState->firstepLevel) + 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, + "level out of range (%u > %u)\n", + 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) + ah->stats.ast_ani_spurup++; + else if (level < aniState->spurImmunityLevel) + ah->stats.ast_ani_spurdown++; + aniState->spurImmunityLevel = level; + break; + } + case ATH9K_ANI_PRESENT: + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "invalid cmd %u\n", cmd); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "ANI parameters:\n"); + 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_hw_update_mibstats(struct ath_hw *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_ani_restart(struct ath_hw *ah) +{ + struct ar5416AniState *aniState; + + if (!DO_ANI(ah)) + return; + + aniState = ah->curani; + + aniState->listenTime = 0; + if (ah->has_hw_phycounters) { + 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, + "Writing ofdmbase=%u cckbase=%u\n", + 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, &ah->ah_mibStats); + } + aniState->ofdmPhyErrCount = 0; + aniState->cckPhyErrCount = 0; +} + +static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) +{ + struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ar5416AniState *aniState; + int32_t rssi; + + if (!DO_ANI(ah)) + return; + + aniState = 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->opmode == NL80211_IFTYPE_AP) { + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + } + return; + } + rssi = BEACON_RSSI(ah); + 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 { + if (conf->channel->band == IEEE80211_BAND_2GHZ) { + 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_hw *ah) +{ + struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ar5416AniState *aniState; + int32_t rssi; + + if (!DO_ANI(ah)) + return; + + aniState = 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->opmode == NL80211_IFTYPE_AP) { + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + } + return; + } + rssi = BEACON_RSSI(ah); + if (rssi > aniState->rssiThrLow) { + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + } else { + if (conf->channel->band == IEEE80211_BAND_2GHZ) { + if (aniState->firstepLevel > 0) + ath9k_hw_ani_control(ah, + ATH9K_ANI_FIRSTEP_LEVEL, 0); + } + } +} + +static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) +{ + struct ar5416AniState *aniState; + int32_t rssi; + + aniState = ah->curani; + + if (ah->opmode == NL80211_IFTYPE_AP) { + if (aniState->firstepLevel > 0) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel - 1)) + return; + } + } else { + rssi = BEACON_RSSI(ah); + 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_hw *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 = ah->curani; + if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { + + listenTime = 0; + 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_ani_reset(struct ath_hw *ah) +{ + struct ar5416AniState *aniState; + struct ath9k_channel *chan = ah->curchan; + int index; + + if (!DO_ANI(ah)) + return; + + index = ath9k_hw_get_ani_channel_idx(ah, chan); + aniState = &ah->ani[index]; + ah->curani = aniState; + + if (DO_ANI(ah) && ah->opmode != NL80211_IFTYPE_STATION + && ah->opmode != NL80211_IFTYPE_ADHOC) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "Reset ANI state opmode %u\n", ah->opmode); + 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->opmode == NL80211_IFTYPE_AP) { + ah->curani->ofdmTrigHigh = + ah->config.ofdm_trig_high; + ah->curani->ofdmTrigLow = + ah->config.ofdm_trig_low; + ah->curani->cckTrigHigh = + ah->config.cck_trig_high; + ah->curani->cckTrigLow = + ah->config.cck_trig_low; + } + 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 (ah->has_hw_phycounters) { + 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_ani_monitor(struct ath_hw *ah, + const struct ath9k_node_stats *stats, + struct ath9k_channel *chan) +{ + struct ar5416AniState *aniState; + int32_t listenTime; + + if (!DO_ANI(ah)) + return; + + aniState = ah->curani; + ah->stats.ast_nodestats = *stats; + + listenTime = ath9k_hw_ani_get_listen_time(ah); + if (listenTime < 0) { + ah->stats.ast_ani_lneg++; + ath9k_ani_restart(ah); + return; + } + + aniState->listenTime += listenTime; + + if (ah->has_hw_phycounters) { + u32 phyCnt1, phyCnt2; + u32 ofdmPhyErrCnt, cckPhyErrCnt; + + ath9k_hw_update_mibstats(ah, &ah->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, + "phyCnt1 0x%x, resetting " + "counter value to 0x%x\n", + 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, + "phyCnt2 0x%x, resetting " + "counter value to 0x%x\n", + 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; + ah->stats.ast_ani_ofdmerrs += + ofdmPhyErrCnt - aniState->ofdmPhyErrCount; + aniState->ofdmPhyErrCount = ofdmPhyErrCnt; + + cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; + ah->stats.ast_ani_cckerrs += + cckPhyErrCnt - aniState->cckPhyErrCount; + aniState->cckPhyErrCount = cckPhyErrCnt; + } + + if (aniState->listenTime > 5 * 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 > 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); + } + } +} + +bool ath9k_hw_phycounters(struct ath_hw *ah) +{ + return ah->has_hw_phycounters ? true : false; +} + +void ath9k_enable_mib_counters(struct ath_hw *ah) +{ + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable MIB counters\n"); + + ath9k_hw_update_mibstats(ah, &ah->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); +} + +/* Freeze the MIB counters, get the stats and then clear them */ +void ath9k_hw_disable_mib_counters(struct ath_hw *ah) +{ + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disable MIB counters\n"); + REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC); + ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); + REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC); + REG_WRITE(ah, AR_FILT_OFDM, 0); + REG_WRITE(ah, AR_FILT_CCK, 0); +} + +u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *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_ANI, + "cycle counter wrap. ExtBusy = 0\n"); + 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; +} + +/* + * Process a MIB interrupt. We may potentially be invoked because + * any of the MIB counters overflow/trigger so don't assume we're + * here because a PHY error counter triggered. + */ +void ath9k_hw_procmibevent(struct ath_hw *ah, + const struct ath9k_node_stats *stats) +{ + u32 phyCnt1, phyCnt2; + + /* Reset these counters regardless */ + 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); + + /* Clear the mib counters and save them in the stats */ + ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); + ah->stats.ast_nodestats = *stats; + + if (!DO_ANI(ah)) + return; + + /* NB: these are not reset-on-read */ + 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 = ah->curani; + u32 ofdmPhyErrCnt, cckPhyErrCnt; + + /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */ + ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; + ah->stats.ast_ani_ofdmerrs += + ofdmPhyErrCnt - aniState->ofdmPhyErrCount; + aniState->ofdmPhyErrCount = ofdmPhyErrCnt; + + cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; + ah->stats.ast_ani_cckerrs += + cckPhyErrCnt - aniState->cckPhyErrCount; + aniState->cckPhyErrCount = cckPhyErrCnt; + + /* + * NB: figure out which counter triggered. If both + * trigger we'll only deal with one as the processing + * clobbers the error counter so the trigger threshold + * check will never be true. + */ + if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh) + ath9k_hw_ani_ofdm_err_trigger(ah); + if (aniState->cckPhyErrCount > aniState->cckTrigHigh) + ath9k_hw_ani_cck_err_trigger(ah); + /* NB: always restart to insure the h/w counters are reset */ + ath9k_ani_restart(ah); + } +} + +void ath9k_hw_ani_setup(struct ath_hw *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++) { + ah->totalSizeDesired[i] = totalSizeDesired[i]; + ah->coarse_high[i] = coarseHigh[i]; + ah->coarse_low[i] = coarseLow[i]; + ah->firpwr[i] = firpwr[i]; + } +} + +void ath9k_hw_ani_attach(struct ath_hw *ah) +{ + int i; + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Attach ANI\n"); + + ah->has_hw_phycounters = 1; + + memset(ah->ani, 0, sizeof(ah->ani)); + for (i = 0; i < ARRAY_SIZE(ah->ani); i++) { + ah->ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH; + ah->ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW; + ah->ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH; + ah->ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW; + ah->ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; + ah->ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; + ah->ani[i].ofdmWeakSigDetectOff = + !ATH9K_ANI_USE_OFDM_WEAK_SIG; + ah->ani[i].cckWeakSigThreshold = + ATH9K_ANI_CCK_WEAK_SIG_THR; + ah->ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; + ah->ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL; + if (ah->has_hw_phycounters) { + ah->ani[i].ofdmPhyErrBase = + AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH; + ah->ani[i].cckPhyErrBase = + AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH; + } + } + if (ah->has_hw_phycounters) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "Setting OfdmErrBase = 0x%08x\n", + ah->ani[0].ofdmPhyErrBase); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n", + ah->ani[0].cckPhyErrBase); + + REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase); + ath9k_enable_mib_counters(ah); + } + ah->aniperiod = ATH9K_ANI_PERIOD; + if (ah->config.enable_ani) + ah->proc_phyerr |= HAL_PROCESS_ANI; +} + +void ath9k_hw_ani_detach(struct ath_hw *ah) +{ + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Detach ANI\n"); + + if (ah->has_hw_phycounters) { + ath9k_hw_disable_mib_counters(ah); + REG_WRITE(ah, AR_PHY_ERR_1, 0); + REG_WRITE(ah, AR_PHY_ERR_2, 0); + } +} diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h new file mode 100644 index 00000000000..08b4e7ed5ff --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2008-2009 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 ANI_H +#define ANI_H + +#define HAL_PROCESS_ANI 0x00000001 +#define ATH9K_RSSI_EP_MULTIPLIER (1<<7) + +#define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI)) + +#define HAL_EP_RND(x, mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) +#define BEACON_RSSI(ahp) \ + HAL_EP_RND(ahp->stats.ast_nodestats.ns_avgbrssi, \ + ATH9K_RSSI_EP_MULTIPLIER) + +#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 HAL_NOISE_IMMUNE_MAX 4 +#define HAL_SPUR_IMMUNE_MAX 7 +#define HAL_FIRST_STEP_MAX 2 + +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 +}; + +struct ath9k_mib_stats { + u32 ackrcv_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 beacons; +}; + +struct ath9k_node_stats { + u32 ns_avgbrssi; + u32 ns_avgrssi; + u32 ns_avgtxrssi; + u32 ns_avgtxrate; +}; + +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]; +}; + +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 ah_mibStats stats.ast_mibstats + +void ath9k_ani_reset(struct ath_hw *ah); +void ath9k_hw_ani_monitor(struct ath_hw *ah, + const struct ath9k_node_stats *stats, + struct ath9k_channel *chan); +bool ath9k_hw_phycounters(struct ath_hw *ah); +void ath9k_enable_mib_counters(struct ath_hw *ah); +void ath9k_hw_disable_mib_counters(struct ath_hw *ah); +u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, u32 *rxc_pcnt, + u32 *rxf_pcnt, u32 *txf_pcnt); +void ath9k_hw_procmibevent(struct ath_hw *ah, + const struct ath9k_node_stats *stats); +void ath9k_hw_ani_setup(struct ath_hw *ah); +void ath9k_hw_ani_attach(struct ath_hw *ah); +void ath9k_hw_ani_detach(struct ath_hw *ah); + +#endif /* ANI_H */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h new file mode 100644 index 00000000000..515880aa211 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -0,0 +1,705 @@ +/* + * Copyright (c) 2008-2009 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 <linux/etherdevice.h> +#include <linux/device.h> +#include <net/mac80211.h> +#include <linux/leds.h> +#include <linux/rfkill.h> + +#include "hw.h" +#include "rc.h" +#include "debug.h" + +struct ath_node; + +/* 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) BUG_ON(!(exp)) + +#define TSF_TO_TU(_h,_l) \ + ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) + +#define ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<<i)) + +static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +struct ath_config { + u32 ath_aggr_prot; + u16 txpowlimit; + u8 cabqReadytime; +}; + +/*************************/ +/* Descriptor Management */ +/*************************/ + +#define ATH_TXBUF_RESET(_bf) do { \ + (_bf)->bf_stale = false; \ + (_bf)->bf_lastbf = NULL; \ + (_bf)->bf_next = NULL; \ + memset(&((_bf)->bf_state), 0, \ + sizeof(struct ath_buf_state)); \ + } while (0) + +#define ATH_RXBUF_RESET(_bf) do { \ + (_bf)->bf_stale = false; \ + } while (0) + +/** + * enum buffer_type - Buffer type flags + * + * @BUF_HT: Send this buffer using HT capabilities + * @BUF_AMPDU: This buffer is an ampdu, as part of an aggregate (during TX) + * @BUF_AGGR: Indicates whether the buffer can be aggregated + * (used in aggregation scheduling) + * @BUF_RETRY: Indicates whether the buffer is retried + * @BUF_XRETRY: To denote excessive retries of the buffer + */ +enum buffer_type { + BUF_HT = BIT(1), + BUF_AMPDU = BIT(2), + BUF_AGGR = BIT(3), + BUF_RETRY = BIT(4), + BUF_XRETRY = BIT(5), +}; + +struct ath_buf_state { + int bfs_nframes; + u16 bfs_al; + u16 bfs_frmlen; + int bfs_seqno; + int bfs_tidno; + int bfs_retries; + u8 bf_type; + u32 bfs_keyix; + 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_keyix bf_state.bfs_keyix +#define bf_keytype bf_state.bfs_keytype +#define bf_isht(bf) (bf->bf_state.bf_type & BUF_HT) +#define bf_isampdu(bf) (bf->bf_state.bf_type & BUF_AMPDU) +#define bf_isaggr(bf) (bf->bf_state.bf_type & BUF_AGGR) +#define bf_isretried(bf) (bf->bf_state.bf_type & BUF_RETRY) +#define bf_isxretried(bf) (bf->bf_state.bf_type & BUF_XRETRY) + +struct ath_buf { + struct list_head list; + struct ath_buf *bf_lastbf; /* last buf of this unit (a frame or + an aggregate) */ + struct ath_buf *bf_next; /* next subframe in the aggregate */ + struct sk_buff *bf_mpdu; /* enclosing frame structure */ + 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 */ + bool bf_stale; + u16 bf_flags; + struct ath_buf_state bf_state; + dma_addr_t bf_dmacontext; +}; + +struct ath_descdma { + struct ath_desc *dd_desc; + dma_addr_t dd_desc_paddr; + u32 dd_desc_len; + struct ath_buf *dd_bufptr; +}; + +int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, + struct list_head *head, const char *name, + int nbuf, int ndesc); +void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, + struct list_head *head); + +/***********/ +/* RX / TX */ +/***********/ + +#define ATH_MAX_ANTENNA 3 +#define ATH_RXBUF 512 +#define WME_NUM_TID 16 +#define ATH_TXBUF 512 +#define ATH_TXMAXTRY 13 +#define ATH_11N_TXMAXTRY 10 +#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) + +#define WME_AC_BE 0 +#define WME_AC_BK 1 +#define WME_AC_VI 2 +#define WME_AC_VO 3 +#define WME_NUM_AC 4 + +#define ADDBA_EXCHANGE_ATTEMPTS 10 +#define ATH_AGGR_DELIM_SZ 4 +#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 ATH_AMPDU_LIMIT_MAX (64 * 1024 - 1) +#define ATH_AMPDU_LIMIT_DEFAULT ATH_AMPDU_LIMIT_MAX + +#define IEEE80211_SEQ_SEQ_SHIFT 4 +#define IEEE80211_SEQ_MAX 4096 +#define IEEE80211_MIN_AMPDU_BUF 0x8 +#define IEEE80211_HTCAP_MAXRXAMPDU_FACTOR 13 +#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)) + +/* 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)->tid[(_tidno)]) + +enum ATH_AGGR_STATUS { + ATH_AGGR_DONE, + ATH_AGGR_BAW_CLOSED, + ATH_AGGR_LIMITED, +}; + +struct ath_txq { + u32 axq_qnum; + u32 *axq_link; + struct list_head axq_q; + spinlock_t axq_lock; + u32 axq_depth; + u8 axq_aggr_depth; + u32 axq_totalqueued; + bool stopped; + struct ath_buf *axq_linkbuf; + + /* first desc of the last descriptor that contains CTS */ + struct ath_desc *axq_lastdsWithCTS; + + /* final desc of the gating desc that determines whether + lastdsWithCTS has been DMA'ed or not */ + struct ath_desc *axq_gatingds; + + struct list_head axq_acq; +}; + +#define AGGR_CLEANUP BIT(1) +#define AGGR_ADDBA_COMPLETE BIT(2) +#define AGGR_ADDBA_PROGRESS BIT(3) + +struct ath_atx_tid { + struct list_head list; + struct list_head buf_q; + struct ath_node *an; + struct ath_atx_ac *ac; + struct ath_buf *tx_buf[ATH_TID_MAX_BUFS]; + u16 seq_start; + u16 seq_next; + u16 baw_size; + int tidno; + int baw_head; /* first un-acked tx buffer */ + int baw_tail; /* next unused tx buffer slot */ + int sched; + int paused; + u8 state; + int addba_exchangeattempts; +}; + +struct ath_atx_ac { + int sched; + int qnum; + struct list_head list; + struct list_head tid_q; +}; + +struct ath_tx_control { + struct ath_txq *txq; + int if_id; + enum ath9k_internal_frame_type frame_type; +}; + +#define ATH_TX_ERROR 0x01 +#define ATH_TX_XRETRY 0x02 +#define ATH_TX_BAR 0x04 + +struct ath_node { + struct ath_softc *an_sc; + struct ath_atx_tid tid[WME_NUM_TID]; + struct ath_atx_ac ac[WME_NUM_AC]; + u16 maxampdu; + u8 mpdudensity; +}; + +struct ath_tx { + u16 seq_no; + u32 txqsetup; + int hwq_map[ATH9K_WME_AC_VO+1]; + spinlock_t txbuflock; + struct list_head txbuf; + struct ath_txq txq[ATH9K_NUM_TX_QUEUES]; + struct ath_descdma txdma; +}; + +struct ath_rx { + u8 defant; + u8 rxotherant; + u32 *rxlink; + int bufsize; + unsigned int rxfilter; + spinlock_t rxflushlock; + spinlock_t rxbuflock; + struct list_head rxbuf; + struct ath_descdma rxdma; +}; + +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); +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); +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_drain_all_txq(struct ath_softc *sc, bool retry_tx); +void ath_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); +void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq); +int ath_tx_init(struct ath_softc *sc, int nbufs); +void ath_tx_cleanup(struct ath_softc *sc); +struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb); +int ath_txq_update(struct ath_softc *sc, int qnum, + struct ath9k_tx_queue_info *q); +int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath_tx_control *txctl); +void ath_tx_tasklet(struct ath_softc *sc); +void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb); +bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno); +int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, + u16 tid, u16 *ssn); +int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); +void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); + +/********/ +/* VIFs */ +/********/ + +struct ath_vif { + int av_bslot; + __le64 tsf_adjust; /* TSF adjustment for staggered beacons */ + enum nl80211_iftype av_opmode; + struct ath_buf *av_bcbuf; + struct ath_tx_control av_btxctl; + u8 bssid[ETH_ALEN]; /* current BSSID from config_interface */ +}; + +/*******************/ +/* 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 +#define ATH_DEFAULT_BINTVAL 100 /* TU */ +#define ATH_DEFAULT_BMISS_LIMIT 10 +#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) + +struct ath_beacon_config { + u16 beacon_interval; + u16 listen_interval; + u16 dtim_period; + u16 bmiss_timeout; + u8 dtim_count; +}; + +struct ath_beacon { + enum { + OK, /* no change needed */ + UPDATE, /* update pending */ + COMMIT /* beacon sent, commit change */ + } updateslot; /* slot time update fsm */ + + u32 beaconq; + u32 bmisscnt; + u32 ast_be_xmit; + u64 bc_tstamp; + struct ieee80211_vif *bslot[ATH_BCBUF]; + struct ath_wiphy *bslot_aphy[ATH_BCBUF]; + int slottime; + int slotupdate; + struct ath9k_tx_queue_info beacon_qi; + struct ath_descdma bdma; + struct ath_txq *cabq; + struct list_head bbuf; +}; + +void ath_beacon_tasklet(unsigned long data); +void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif); +int ath_beaconq_setup(struct ath_hw *ah); +int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif); +void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp); + +/*******/ +/* ANI */ +/*******/ + +#define ATH_STA_SHORT_CALINTERVAL 1000 /* 1 second */ +#define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */ +#define ATH_ANI_POLLINTERVAL 100 /* 100 ms */ +#define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ +#define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ + +struct ath_ani { + bool caldone; + int16_t noise_floor; + unsigned int longcal_timer; + unsigned int shortcal_timer; + unsigned int resetcal_timer; + unsigned int checkani_timer; + struct timer_list timer; +}; + +/********************/ +/* LED Control */ +/********************/ + +#define ATH_LED_PIN 1 +#define ATH_LED_ON_DURATION_IDLE 350 /* in msecs */ +#define ATH_LED_OFF_DURATION_IDLE 250 /* in msecs */ + +enum ath_led_type { + ATH_LED_RADIO, + ATH_LED_ASSOC, + ATH_LED_TX, + ATH_LED_RX +}; + +struct ath_led { + struct ath_softc *sc; + struct led_classdev led_cdev; + enum ath_led_type led_type; + char name[32]; + bool registered; +}; + +struct ath_rfkill { + struct rfkill *rfkill; + struct rfkill_ops ops; + char rfkill_name[32]; +}; + +/********************/ +/* 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_MAX_SW_RETRIES 10 +#define ATH_CHAN_MAX 255 +#define IEEE80211_WEP_NKID 4 /* number of key ids */ + +/* + * 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 ATH_TXPOWER_MAX 100 /* .5 dBm units */ +#define ATH_RSSI_DUMMY_MARKER 0x127 +#define ATH_RATE_DUMMY_MARKER 0 + +#define SC_OP_INVALID BIT(0) +#define SC_OP_BEACONS BIT(1) +#define SC_OP_RXAGGR BIT(2) +#define SC_OP_TXAGGR BIT(3) +#define SC_OP_FULL_RESET BIT(4) +#define SC_OP_PREAMBLE_SHORT BIT(5) +#define SC_OP_PROTECT_ENABLE BIT(6) +#define SC_OP_RXFLUSH BIT(7) +#define SC_OP_LED_ASSOCIATED BIT(8) +#define SC_OP_RFKILL_REGISTERED BIT(9) +#define SC_OP_WAIT_FOR_BEACON BIT(12) +#define SC_OP_LED_ON BIT(13) +#define SC_OP_SCANNING BIT(14) +#define SC_OP_TSF_RESET BIT(15) +#define SC_OP_WAIT_FOR_CAB BIT(16) +#define SC_OP_WAIT_FOR_PSPOLL_DATA BIT(17) +#define SC_OP_WAIT_FOR_TX_ACK BIT(18) +#define SC_OP_BEACON_SYNC BIT(19) + +struct ath_bus_ops { + void (*read_cachesize)(struct ath_softc *sc, int *csz); + void (*cleanup)(struct ath_softc *sc); + bool (*eeprom_read)(struct ath_hw *ah, u32 off, u16 *data); +}; + +struct ath_wiphy; + +struct ath_softc { + struct ieee80211_hw *hw; + struct device *dev; + + spinlock_t wiphy_lock; /* spinlock to protect ath_wiphy data */ + struct ath_wiphy *pri_wiphy; + struct ath_wiphy **sec_wiphy; /* secondary wiphys (virtual radios); may + * have NULL entries */ + int num_sec_wiphy; /* number of sec_wiphy pointers in the array */ + int chan_idx; + int chan_is_ht; + struct ath_wiphy *next_wiphy; + struct work_struct chan_work; + int wiphy_select_failures; + unsigned long wiphy_select_first_fail; + struct delayed_work wiphy_work; + unsigned long wiphy_scheduler_int; + int wiphy_scheduler_index; + + struct tasklet_struct intr_tq; + struct tasklet_struct bcon_tasklet; + struct ath_hw *sc_ah; + void __iomem *mem; + int irq; + spinlock_t sc_resetlock; + spinlock_t sc_serial_rw; + struct mutex mutex; + + u8 curbssid[ETH_ALEN]; + u8 bssidmask[ETH_ALEN]; + u32 intrstatus; + u32 sc_flags; /* SC_OP_* */ + u16 curtxpow; + u16 curaid; + u16 cachelsz; + u8 nbcnvifs; + u16 nvifs; + u8 tx_chainmask; + u8 rx_chainmask; + u32 keymax; + DECLARE_BITMAP(keymap, ATH_KEYMAX); + u8 splitmic; + atomic_t ps_usecount; + enum ath9k_int imask; + enum ath9k_ht_extprotspacing ht_extprotspacing; + enum ath9k_ht_macmode tx_chan_width; + + struct ath_config config; + struct ath_rx rx; + struct ath_tx tx; + struct ath_beacon beacon; + struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX]; + const struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX]; + const struct ath_rate_table *cur_rate_table; + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + + struct ath_led radio_led; + struct ath_led assoc_led; + struct ath_led tx_led; + struct ath_led rx_led; + struct delayed_work ath_led_blink_work; + int led_on_duration; + int led_off_duration; + int led_on_cnt; + int led_off_cnt; + + int beacon_interval; + + struct ath_rfkill rf_kill; + struct ath_ani ani; + struct ath9k_node_stats nodestats; +#ifdef CONFIG_ATH9K_DEBUG + struct ath9k_debug debug; +#endif + struct ath_bus_ops *bus_ops; + struct ath_beacon_config cur_beacon_conf; +}; + +struct ath_wiphy { + struct ath_softc *sc; /* shared for all virtual wiphys */ + struct ieee80211_hw *hw; + enum ath_wiphy_state { + ATH_WIPHY_INACTIVE, + ATH_WIPHY_ACTIVE, + ATH_WIPHY_PAUSING, + ATH_WIPHY_PAUSED, + ATH_WIPHY_SCAN, + } state; + int chan_idx; + int chan_is_ht; +}; + +int ath_reset(struct ath_softc *sc, bool retry_tx); +int ath_get_hal_qnum(u16 queue, struct ath_softc *sc); +int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc); +int ath_cabq_update(struct ath_softc *); + +static inline void ath_read_cachesize(struct ath_softc *sc, int *csz) +{ + sc->bus_ops->read_cachesize(sc, csz); +} + +static inline void ath_bus_cleanup(struct ath_softc *sc) +{ + sc->bus_ops->cleanup(sc); +} + +extern struct ieee80211_ops ath9k_ops; + +irqreturn_t ath_isr(int irq, void *dev); +void ath_cleanup(struct ath_softc *sc); +int ath_attach(u16 devid, struct ath_softc *sc); +void ath_detach(struct ath_softc *sc); +const char *ath_mac_bb_name(u32 mac_bb_version); +const char *ath_rf_name(u16 rf_version); +void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); +void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, + struct ath9k_channel *ichan); +void ath_update_chainmask(struct ath_softc *sc, int is_ht); +int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, + struct ath9k_channel *hchan); +void ath_radio_enable(struct ath_softc *sc); +void ath_radio_disable(struct ath_softc *sc); + +#ifdef CONFIG_PCI +int ath_pci_init(void); +void ath_pci_exit(void); +#else +static inline int ath_pci_init(void) { return 0; }; +static inline void ath_pci_exit(void) {}; +#endif + +#ifdef CONFIG_ATHEROS_AR71XX +int ath_ahb_init(void); +void ath_ahb_exit(void); +#else +static inline int ath_ahb_init(void) { return 0; }; +static inline void ath_ahb_exit(void) {}; +#endif + +static inline void ath9k_ps_wakeup(struct ath_softc *sc) +{ + if (atomic_inc_return(&sc->ps_usecount) == 1) + if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) { + sc->sc_ah->restore_mode = sc->sc_ah->power_mode; + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + } +} + +static inline void ath9k_ps_restore(struct ath_softc *sc) +{ + if (atomic_dec_and_test(&sc->ps_usecount)) + if ((sc->hw->conf.flags & IEEE80211_CONF_PS) && + !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | + SC_OP_WAIT_FOR_PSPOLL_DATA | + SC_OP_WAIT_FOR_TX_ACK))) + ath9k_hw_setpower(sc->sc_ah, + sc->sc_ah->restore_mode); +} + + +void ath9k_set_bssid_mask(struct ieee80211_hw *hw); +int ath9k_wiphy_add(struct ath_softc *sc); +int ath9k_wiphy_del(struct ath_wiphy *aphy); +void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb); +int ath9k_wiphy_pause(struct ath_wiphy *aphy); +int ath9k_wiphy_unpause(struct ath_wiphy *aphy); +int ath9k_wiphy_select(struct ath_wiphy *aphy); +void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int); +void ath9k_wiphy_chan_work(struct work_struct *work); +bool ath9k_wiphy_started(struct ath_softc *sc); +void ath9k_wiphy_pause_all_forced(struct ath_softc *sc, + struct ath_wiphy *selected); +bool ath9k_wiphy_scanning(struct ath_softc *sc); +void ath9k_wiphy_work(struct work_struct *work); + +void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val); +unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset); + +#endif /* ATH9K_H */ diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c new file mode 100644 index 00000000000..3639a2e6987 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -0,0 +1,767 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +#define FUDGE 2 + +/* + * 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_hw *ah = sc->sc_ah; + struct ath9k_tx_queue_info qi; + + ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); + if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { + /* 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->beacon.beacon_qi.tqi_aifs; + qi.tqi_cwmin = 2*sc->beacon.beacon_qi.tqi_cwmin; + qi.tqi_cwmax = sc->beacon.beacon_qi.tqi_cwmax; + } + + if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to update h/w beacon queue parameters\n"); + return 0; + } else { + ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); + return 1; + } +} + +/* + * 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_vif *avp, + struct ath_buf *bf) +{ + struct sk_buff *skb = bf->bf_mpdu; + struct ath_hw *ah = sc->sc_ah; + struct ath_desc *ds; + struct ath9k_11n_rate_series series[4]; + const struct ath_rate_table *rt; + int flags, antenna, ctsrate = 0, ctsduration = 0; + u8 rate; + + ds = bf->bf_desc; + flags = ATH9K_TXDESC_NOACK; + + if (((sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) || + (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) && + (ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) { + 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 + * XXX assumes two antennae + */ + antenna = ((sc->beacon.ast_be_xmit / sc->nbcnvifs) & 1 ? 2 : 1); + } + + ds->ds_data = bf->bf_buf_addr; + + rt = sc->cur_rate_table; + rate = rt->info[0].ratecode; + if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) + rate |= rt->info[0].short_preamble; + + ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN, + ATH9K_PKT_TYPE_BEACON, + MAX_RATE_POWER, + ATH9K_TXKEYIX_INVALID, + ATH9K_KEY_TYPE_CLEAR, + flags); + + /* NB: beacon's BufLen must be a multiple of 4 bytes */ + ath9k_hw_filltxdesc(ah, ds, roundup(skb->len, 4), + true, true, ds); + + memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4); + series[0].Tries = 1; + series[0].Rate = rate; + series[0].ChSel = 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); +} + +static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_buf *bf; + struct ath_vif *avp; + struct sk_buff *skb; + struct ath_txq *cabq; + struct ieee80211_tx_info *info; + int cabq_depth; + + if (aphy->state != ATH_WIPHY_ACTIVE) + return NULL; + + avp = (void *)vif->drv_priv; + cabq = sc->beacon.cabq; + + if (avp->av_bcbuf == NULL) + return NULL; + + /* Release the old beacon first */ + + bf = avp->av_bcbuf; + skb = bf->bf_mpdu; + if (skb) { + dma_unmap_single(sc->dev, bf->bf_dmacontext, + skb->len, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + } + + /* Get a new beacon from mac80211 */ + + skb = ieee80211_beacon_get(hw, vif); + bf->bf_mpdu = skb; + if (skb == NULL) + return NULL; + ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = + avp->tsf_adjust; + + info = IEEE80211_SKB_CB(skb); + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + /* + * TODO: make sure the seq# gets assigned properly (vs. other + * TX frames) + */ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + sc->tx.seq_no += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); + } + + bf->bf_buf_addr = bf->bf_dmacontext = + dma_map_single(sc->dev, skb->data, + skb->len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + DPRINTF(sc, ATH_DBG_FATAL, "dma_mapping_error on beaconing\n"); + return NULL; + } + + skb = ieee80211_get_buffered_bc(hw, vif); + + /* + * if the CABQ traffic from previous DTIM is pending and the current + * beacon is also a DTIM. + * 1) if there is only one vif let the cab traffic continue. + * 2) if there are more than one vif and we are using staggered + * beacons, then drain the cabq by dropping all the frames in + * the cabq so that the current vifs cab traffic can be scheduled. + */ + spin_lock_bh(&cabq->axq_lock); + cabq_depth = cabq->axq_depth; + spin_unlock_bh(&cabq->axq_lock); + + if (skb && cabq_depth) { + if (sc->nvifs > 1) { + DPRINTF(sc, ATH_DBG_BEACON, + "Flushing previous cabq traffic\n"); + ath_draintxq(sc, cabq, false); + } + } + + ath_beacon_setup(sc, avp, bf); + + while (skb) { + ath_tx_cabq(hw, skb); + skb = ieee80211_get_buffered_bc(hw, vif); + } + + 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, + struct ieee80211_vif *vif) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_buf *bf; + struct ath_vif *avp; + struct sk_buff *skb; + + avp = (void *)vif->drv_priv; + + if (avp->av_bcbuf == NULL) + return; + + bf = avp->av_bcbuf; + skb = bf->bf_mpdu; + + ath_beacon_setup(sc, avp, bf); + + /* NB: caller is known to have already stopped tx dma */ + ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr); + ath9k_hw_txstart(ah, sc->beacon.beaconq); + DPRINTF(sc, ATH_DBG_BEACON, "TXDP%u = %llx (%p)\n", + sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc); +} + +int ath_beaconq_setup(struct ath_hw *ah) +{ + struct ath9k_tx_queue_info qi; + + memset(&qi, 0, 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); +} + +int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) +{ + struct ath_softc *sc = aphy->sc; + struct ath_vif *avp; + struct ath_buf *bf; + struct sk_buff *skb; + __le64 tstamp; + + avp = (void *)vif->drv_priv; + + /* 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->beacon.bbuf, + struct ath_buf, list); + list_del(&avp->av_bcbuf->list); + + if (sc->sc_ah->opmode == NL80211_IFTYPE_AP || + !(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) { + int slot; + /* + * Assign the vif 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->beacon.bslot[slot] == NULL) { + /* + * XXX hack, space out slots to better + * deal with misses + */ + if (slot+1 < ATH_BCBUF && + sc->beacon.bslot[slot+1] == NULL) { + avp->av_bslot = slot+1; + break; + } + avp->av_bslot = slot; + /* NB: keep looking for a double slot */ + } + BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL); + sc->beacon.bslot[avp->av_bslot] = vif; + sc->beacon.bslot_aphy[avp->av_bslot] = aphy; + sc->nbcnvifs++; + } + } + + /* release the previous beacon frame, if it already exists. */ + bf = avp->av_bcbuf; + if (bf->bf_mpdu != NULL) { + skb = bf->bf_mpdu; + dma_unmap_single(sc->dev, bf->bf_dmacontext, + skb->len, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + } + + /* NB: the beacon data buffer must be 32-bit aligned. */ + skb = ieee80211_beacon_get(sc->hw, vif); + if (skb == NULL) { + DPRINTF(sc, ATH_DBG_BEACON, "cannot get skb\n"); + return -ENOMEM; + } + + tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; + sc->beacon.bc_tstamp = le64_to_cpu(tstamp); + /* Calculate a TSF adjustment factor required for staggered beacons. */ + if (avp->av_bslot > 0) { + u64 tsfadjust; + int intval; + + intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; + + /* + * Calculate the TSF offset for this beacon slot, i.e., the + * number of usecs that need to be added to the timestamp field + * in Beacon and Probe Response frames. Beacon slot 0 is + * processed at the correct offset, so it does not require TSF + * adjustment. Other slots are adjusted to get the timestamp + * close to the TBTT for the BSS. + */ + tsfadjust = intval * avp->av_bslot / ATH_BCBUF; + avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); + + DPRINTF(sc, ATH_DBG_BEACON, + "stagger beacons, bslot %d intval %u tsfadjust %llu\n", + avp->av_bslot, intval, (unsigned long long)tsfadjust); + + ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = + avp->tsf_adjust; + } else + avp->tsf_adjust = cpu_to_le64(0); + + bf->bf_mpdu = skb; + bf->bf_buf_addr = bf->bf_dmacontext = + dma_map_single(sc->dev, skb->data, + skb->len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + DPRINTF(sc, ATH_DBG_FATAL, + "dma_mapping_error on beacon alloc\n"); + return -ENOMEM; + } + + return 0; +} + +void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp) +{ + if (avp->av_bcbuf != NULL) { + struct ath_buf *bf; + + if (avp->av_bslot != -1) { + sc->beacon.bslot[avp->av_bslot] = NULL; + sc->beacon.bslot_aphy[avp->av_bslot] = NULL; + sc->nbcnvifs--; + } + + bf = avp->av_bcbuf; + if (bf->bf_mpdu != NULL) { + struct sk_buff *skb = bf->bf_mpdu; + dma_unmap_single(sc->dev, bf->bf_dmacontext, + skb->len, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + } + list_add_tail(&bf->list, &sc->beacon.bbuf); + + avp->av_bcbuf = NULL; + } +} + +void ath_beacon_tasklet(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + struct ath_hw *ah = sc->sc_ah; + struct ath_buf *bf = NULL; + struct ieee80211_vif *vif; + struct ath_wiphy *aphy; + int slot; + u32 bfaddr, bc = 0, tsftu; + u64 tsf; + u16 intval; + + /* + * 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->beacon.beaconq) != 0) { + sc->beacon.bmisscnt++; + + if (sc->beacon.bmisscnt < BSTUCK_THRESH) { + DPRINTF(sc, ATH_DBG_BEACON, + "missed %u consecutive beacons\n", + sc->beacon.bmisscnt); + } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { + DPRINTF(sc, ATH_DBG_BEACON, + "beacon is officially stuck\n"); + sc->sc_flags |= SC_OP_TSF_RESET; + ath_reset(sc, false); + } + + return; + } + + if (sc->beacon.bmisscnt != 0) { + DPRINTF(sc, ATH_DBG_BEACON, + "resume beacon xmit after %u misses\n", + sc->beacon.bmisscnt); + sc->beacon.bmisscnt = 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. + */ + + intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; + + tsf = ath9k_hw_gettsf64(ah); + tsftu = TSF_TO_TU(tsf>>32, tsf); + slot = ((tsftu % intval) * ATH_BCBUF) / intval; + /* + * Reverse the slot order to get slot 0 on the TBTT offset that does + * not require TSF adjustment and other slots adding + * slot/ATH_BCBUF * beacon_int to timestamp. For example, with + * ATH_BCBUF = 4, we process beacon slots as follows: 3 2 1 0 3 2 1 .. + * and slot 0 is at correct offset to TBTT. + */ + slot = ATH_BCBUF - slot - 1; + vif = sc->beacon.bslot[slot]; + aphy = sc->beacon.bslot_aphy[slot]; + + DPRINTF(sc, ATH_DBG_BEACON, + "slot %d [tsf %llu tsftu %u intval %u] vif %p\n", + slot, tsf, tsftu, intval, vif); + + bfaddr = 0; + if (vif) { + bf = ath_beacon_generate(aphy->hw, vif); + 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. + */ + if (sc->beacon.updateslot == UPDATE) { + sc->beacon.updateslot = COMMIT; /* commit next beacon */ + sc->beacon.slotupdate = slot; + } else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) { + ath9k_hw_setslottime(sc->sc_ah, sc->beacon.slottime); + sc->beacon.updateslot = OK; + } + 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->beacon.beaconq)) { + DPRINTF(sc, ATH_DBG_FATAL, + "beacon queue %u did not stop?\n", sc->beacon.beaconq); + } + + /* NB: cabq traffic should already be queued and primed */ + ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr); + ath9k_hw_txstart(ah, sc->beacon.beaconq); + + sc->beacon.ast_be_xmit += bc; /* XXX per-vif? */ + } +} + +/* + * 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. + */ +static void ath_beacon_config_ap(struct ath_softc *sc, + struct ath_beacon_config *conf) +{ + u32 nexttbtt, intval; + + /* Configure the timers only when the TSF has to be reset */ + + if (!(sc->sc_flags & SC_OP_TSF_RESET)) + return; + + /* NB: the beacon interval is kept internally in TU's */ + intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; + intval /= ATH_BCBUF; /* for staggered beacons */ + nexttbtt = intval; + intval |= ATH9K_BEACON_RESET_TSF; + + /* + * In AP mode we enable the beacon timers and SWBA interrupts to + * prepare beacon frames. + */ + intval |= ATH9K_BEACON_ENA; + sc->imask |= ATH9K_INT_SWBA; + ath_beaconq_config(sc); + + /* Set the computed AP beacon timers */ + + ath9k_hw_set_interrupts(sc->sc_ah, 0); + ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval); + sc->beacon.bmisscnt = 0; + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); + + /* Clear the reset TSF flag, so that subsequent beacon updation + will not reset the HW TSF. */ + + sc->sc_flags &= ~SC_OP_TSF_RESET; +} + +/* + * 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. + */ +static void ath_beacon_config_sta(struct ath_softc *sc, + struct ath_beacon_config *conf) +{ + struct ath9k_beacon_state bs; + int dtimperiod, dtimcount, sleepduration; + int cfpperiod, cfpcount; + u32 nexttbtt = 0, intval, tsftu; + u64 tsf; + int num_beacons, offset, dtim_dec_count, cfp_dec_count; + + memset(&bs, 0, sizeof(bs)); + intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; + + /* + * 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; + cfpperiod = 1; /* NB: no PCF support yet */ + cfpcount = 0; + + sleepduration = conf->listen_interval * intval; + if (sleepduration <= 0) + sleepduration = intval; + + /* + * Pull nexttbtt forward to reflect the current + * TSF and calculate dtim+cfp state for the result. + */ + tsf = ath9k_hw_gettsf64(sc->sc_ah); + tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; + + num_beacons = tsftu / intval + 1; + offset = tsftu % intval; + nexttbtt = tsftu - offset; + if (offset) + nexttbtt += intval; + + /* DTIM Beacon every dtimperiod Beacon */ + dtim_dec_count = num_beacons % dtimperiod; + /* CFP every cfpperiod DTIM Beacon */ + cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; + if (dtim_dec_count) + cfp_dec_count++; + + dtimcount -= dtim_dec_count; + if (dtimcount < 0) + dtimcount += dtimperiod; + + cfpcount -= cfp_dec_count; + if (cfpcount < 0) + cfpcount += cfpperiod; + + 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 ensure 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; + + /* TSF out of range threshold fixed at 1 second */ + bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; + + DPRINTF(sc, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); + DPRINTF(sc, ATH_DBG_BEACON, + "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", + bs.bs_bmissthreshold, bs.bs_sleepduration, + bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); + + /* Set the computed STA beacon timers */ + + ath9k_hw_set_interrupts(sc->sc_ah, 0); + ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs); + sc->imask |= ATH9K_INT_BMISS; + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); +} + +static void ath_beacon_config_adhoc(struct ath_softc *sc, + struct ath_beacon_config *conf, + struct ieee80211_vif *vif) +{ + u64 tsf; + u32 tsftu, intval, nexttbtt; + + intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; + + /* + * It looks like mac80211 may end up using beacon interval of zero in + * some cases (at least for mesh point). Avoid getting into an + * infinite loop by using a bit safer value instead.. + */ + if (intval == 0) + intval = 100; + + /* Pull nexttbtt forward to reflect the current TSF */ + + nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp); + if (nexttbtt == 0) + nexttbtt = intval; + else if (intval) + nexttbtt = roundup(nexttbtt, intval); + + tsf = ath9k_hw_gettsf64(sc->sc_ah); + tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE; + do { + nexttbtt += intval; + } while (nexttbtt < tsftu); + + DPRINTF(sc, ATH_DBG_BEACON, + "IBSS nexttbtt %u intval %u (%u)\n", + nexttbtt, intval, 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 (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) + sc->imask |= ATH9K_INT_SWBA; + + ath_beaconq_config(sc); + + /* Set the computed ADHOC beacon timers */ + + ath9k_hw_set_interrupts(sc->sc_ah, 0); + ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval); + sc->beacon.bmisscnt = 0; + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); + + /* FIXME: Handle properly when vif is NULL */ + if (vif && sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL) + ath_beacon_start_adhoc(sc, vif); +} + +void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) +{ + struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + enum nl80211_iftype iftype; + + /* Setup the beacon configuration parameters */ + + if (vif) { + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + + iftype = vif->type; + + cur_conf->beacon_interval = bss_conf->beacon_int; + cur_conf->dtim_period = bss_conf->dtim_period; + cur_conf->listen_interval = 1; + cur_conf->dtim_count = 1; + cur_conf->bmiss_timeout = + ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; + } else { + iftype = sc->sc_ah->opmode; + } + + + switch (iftype) { + case NL80211_IFTYPE_AP: + ath_beacon_config_ap(sc, cur_conf); + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + ath_beacon_config_adhoc(sc, cur_conf, vif); + break; + case NL80211_IFTYPE_STATION: + ath_beacon_config_sta(sc, cur_conf); + break; + default: + DPRINTF(sc, ATH_DBG_CONFIG, + "Unsupported beaconing mode\n"); + return; + } + + sc->sc_flags |= SC_OP_BEACONS; +} diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c new file mode 100644 index 00000000000..a32d7e7fecb --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -0,0 +1,1039 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +/* We can tune this as we go by monitoring really low values */ +#define ATH9K_NF_TOO_LOW -60 + +/* AR5416 may return very high value (like -31 dBm), in those cases the nf + * is incorrect and we should use the static NF value. Later we can try to + * find out why they are reporting these values */ + +static bool ath9k_hw_nf_in_range(struct ath_hw *ah, s16 nf) +{ + if (nf > ATH9K_NF_TOO_LOW) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "noise floor value detected (%d) is " + "lower than what we think is a " + "reasonable value (%d)\n", + nf, ATH9K_NF_TOO_LOW); + return false; + } + return true; +} + +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 ath9k_hw_do_getnf(struct ath_hw *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_9285(ah)) { + 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_CALIBRATE, + "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_CALIBRATE, + "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_CALIBRATE, + "NF calibrated [ext] [chain 0] is %d\n", nf); + nfarray[3] = nf; + + if (!AR_SREV_9285(ah)) { + 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_CALIBRATE, + "NF calibrated [ext] [chain 2] is %d\n", nf); + nfarray[5] = nf; + } + } +} + +static bool getNoiseFloorThresh(struct ath_hw *ah, + enum ieee80211_band band, + int16_t *nft) +{ + switch (band) { + case IEEE80211_BAND_5GHZ: + *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5); + break; + case IEEE80211_BAND_2GHZ: + *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2); + break; + default: + BUG_ON(1); + return false; + } + + return true; +} + +static void ath9k_hw_setup_calibration(struct ath_hw *ah, + struct ath9k_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, + "starting IQ Mismatch Calibration\n"); + break; + case ADC_GAIN_CAL: + REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "starting ADC Gain Calibration\n"); + break; + case ADC_DC_CAL: + REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "starting ADC DC Calibration\n"); + 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, + "starting Init ADC DC Calibration\n"); + break; + } + + REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0), + AR_PHY_TIMING_CTRL4_DO_CAL); +} + +static void ath9k_hw_reset_calibration(struct ath_hw *ah, + struct ath9k_cal_list *currCal) +{ + int i; + + ath9k_hw_setup_calibration(ah, currCal); + + currCal->calState = CAL_RUNNING; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ah->meas0.sign[i] = 0; + ah->meas1.sign[i] = 0; + ah->meas2.sign[i] = 0; + ah->meas3.sign[i] = 0; + } + + ah->cal_samples = 0; +} + +static bool ath9k_hw_per_calibration(struct ath_hw *ah, + struct ath9k_channel *ichan, + u8 rxchainmask, + struct ath9k_cal_list *currCal) +{ + bool 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); + ah->cal_samples++; + + if (ah->cal_samples >= 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); + } + + return iscaldone; +} + +/* Assumes you are talking about the currently configured channel */ +static bool ath9k_hw_iscal_supported(struct ath_hw *ah, + enum ath9k_cal_types calType) +{ + struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + + switch (calType & ah->supp_cals) { + case IQ_MISMATCH_CAL: /* Both 2 GHz and 5 GHz support OFDM */ + return true; + case ADC_GAIN_CAL: + case ADC_DC_CAL: + if (!(conf->channel->band == IEEE80211_BAND_2GHZ && + conf_is_ht20(conf))) + return true; + break; + } + return false; +} + +static void ath9k_hw_iqcal_collect(struct ath_hw *ah) +{ + int i; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ah->totalPowerMeasI[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); + ah->totalPowerMeasQ[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); + 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", + ah->cal_samples, i, ah->totalPowerMeasI[i], + ah->totalPowerMeasQ[i], + ah->totalIqCorrMeas[i]); + } +} + +static void ath9k_hw_adc_gaincal_collect(struct ath_hw *ah) +{ + int i; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ah->totalAdcIOddPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); + ah->totalAdcIEvenPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); + ah->totalAdcQOddPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); + 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", + ah->cal_samples, i, + ah->totalAdcIOddPhase[i], + ah->totalAdcIEvenPhase[i], + ah->totalAdcQOddPhase[i], + ah->totalAdcQEvenPhase[i]); + } +} + +static void ath9k_hw_adc_dccal_collect(struct ath_hw *ah) +{ + int i; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ah->totalAdcDcOffsetIOddPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); + ah->totalAdcDcOffsetIEvenPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); + ah->totalAdcDcOffsetQOddPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); + 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", + ah->cal_samples, i, + ah->totalAdcDcOffsetIOddPhase[i], + ah->totalAdcDcOffsetIEvenPhase[i], + ah->totalAdcDcOffsetQOddPhase[i], + ah->totalAdcDcOffsetQEvenPhase[i]); + } +} + +static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) +{ + u32 powerMeasQ, powerMeasI, iqCorrMeas; + u32 qCoffDenom, iCoffDenom; + int32_t qCoff, iCoff; + int iqCorrNeg, i; + + for (i = 0; i < numChains; i++) { + powerMeasI = ah->totalPowerMeasI[i]; + powerMeasQ = ah->totalPowerMeasQ[i]; + iqCorrMeas = 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, 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_hw *ah, u8 numChains) +{ + u32 iOddMeasOffset, iEvenMeasOffset, qOddMeasOffset, qEvenMeasOffset; + u32 qGainMismatch, iGainMismatch, val, i; + + for (i = 0; i < numChains; i++) { + iOddMeasOffset = ah->totalAdcIOddPhase[i]; + iEvenMeasOffset = ah->totalAdcIEvenPhase[i]; + qOddMeasOffset = ah->totalAdcQOddPhase[i]; + qEvenMeasOffset = 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_hw *ah, u8 numChains) +{ + u32 iOddMeasOffset, iEvenMeasOffset, val, i; + int32_t qOddMeasOffset, qEvenMeasOffset, qDcMismatch, iDcMismatch; + const struct ath9k_percal_data *calData = + ah->cal_list_curr->calData; + u32 numSamples = + (1 << (calData->calCountMax + 5)) * calData->calNumSamples; + + for (i = 0; i < numChains; i++) { + iOddMeasOffset = ah->totalAdcDcOffsetIOddPhase[i]; + iEvenMeasOffset = ah->totalAdcDcOffsetIEvenPhase[i]; + qOddMeasOffset = ah->totalAdcDcOffsetQOddPhase[i]; + qEvenMeasOffset = 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); +} + +/* This is done for the currently configured channel */ +bool ath9k_hw_reset_calvalid(struct ath_hw *ah) +{ + struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ath9k_cal_list *currCal = ah->cal_list_curr; + + if (!ah->curchan) + return true; + + if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah)) + return true; + + if (currCal == NULL) + return true; + + if (currCal->calState != CAL_DONE) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Calibration state incorrect, %d\n", + currCal->calState); + return true; + } + + if (!ath9k_hw_iscal_supported(ah, currCal->calData->calType)) + return true; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Resetting Cal %d state for channel %u\n", + currCal->calData->calType, conf->channel->center_freq); + + ah->curchan->CalValid &= ~currCal->calData->calType; + currCal->calState = CAL_WAITING; + + return false; +} + +void ath9k_hw_start_nfcal(struct ath_hw *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); +} + +void ath9k_hw_loadnf(struct ath_hw *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_9285(ah)) + chainmask = 0x9; + else if (AR_SREV_9280(ah)) + chainmask = 0x1B; + else + chainmask = 0x3F; + + h = ah->nfCalHist; + + 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); + } + } +} + +int16_t ath9k_hw_getnf(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + int16_t nf, nfThresh; + int16_t nfarray[NUM_NF_READINGS] = { 0 }; + struct ath9k_nfcal_hist *h; + struct ieee80211_channel *c = chan->chan; + + 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, + "NF did not complete in calibration window\n"); + nf = 0; + chan->rawNoiseFloor = nf; + return chan->rawNoiseFloor; + } else { + ath9k_hw_do_getnf(ah, nfarray); + nf = nfarray[0]; + if (getNoiseFloorThresh(ah, c->band, &nfThresh) + && nf > nfThresh) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "noise floor failed detected; " + "detected %d, threshold %d\n", + nf, nfThresh); + chan->channelFlags |= CHANNEL_CW_INT; + } + } + + h = ah->nfCalHist; + + ath9k_hw_update_nfcal_hist_buffer(h, nfarray); + chan->rawNoiseFloor = h[0].privNF; + + return chan->rawNoiseFloor; +} + +void ath9k_init_nfcal_hist_buffer(struct ath_hw *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; + } + } +} + +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) +{ + s16 nf; + + if (chan->rawNoiseFloor == 0) + nf = -96; + else + nf = chan->rawNoiseFloor; + + if (!ath9k_hw_nf_in_range(ah, nf)) + nf = ATH_DEFAULT_NOISE_FLOOR; + + return nf; +} + +static void ath9k_olc_temp_compensation(struct ath_hw *ah) +{ + u32 rddata, i; + int delta, currPDADC, regval; + + rddata = REG_READ(ah, AR_PHY_TX_PWRCTRL4); + + currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); + + if (ah->eep_ops->get_eeprom(ah, EEP_DAC_HPWR_5G)) + delta = (currPDADC - ah->initPDADC + 4) / 8; + else + delta = (currPDADC - ah->initPDADC + 5) / 10; + + if (delta != ah->PDADCdelta) { + ah->PDADCdelta = delta; + for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { + regval = ah->originalGain[i] - delta; + if (regval < 0) + regval = 0; + + REG_RMW_FIELD(ah, AR_PHY_TX_GAIN_TBL1 + i * 4, + AR_PHY_TX_GAIN, regval); + } + } +} + +static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah) +{ + + u32 regVal; + int i, offset, offs_6_1, offs_0; + u32 ccomp_org, reg_field; + u32 regList[][2] = { + { 0x786c, 0 }, + { 0x7854, 0 }, + { 0x7820, 0 }, + { 0x7824, 0 }, + { 0x7868, 0 }, + { 0x783c, 0 }, + { 0x7838, 0 }, + }; + + if (AR_SREV_9285_11(ah)) { + REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14)); + udelay(10); + } + + for (i = 0; i < ARRAY_SIZE(regList); i++) + regList[i][1] = REG_READ(ah, regList[i][0]); + + regVal = REG_READ(ah, 0x7834); + regVal &= (~(0x1)); + REG_WRITE(ah, 0x7834, regVal); + regVal = REG_READ(ah, 0x9808); + regVal |= (0x1 << 27); + REG_WRITE(ah, 0x9808, regVal); + + REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1); + REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1); + REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1); + REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1); + REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0); + REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0); + REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0); + REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 1); + REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0); + REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0); + REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); + REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); + ccomp_org = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_CCOMP); + REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, 7); + + REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0); + udelay(30); + REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, 0); + REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 0); + + for (i = 6; i > 0; i--) { + regVal = REG_READ(ah, 0x7834); + regVal |= (1 << (19 + i)); + REG_WRITE(ah, 0x7834, regVal); + udelay(1); + regVal = REG_READ(ah, 0x7834); + regVal &= (~(0x1 << (19 + i))); + reg_field = MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9); + regVal |= (reg_field << (19 + i)); + REG_WRITE(ah, 0x7834, regVal); + } + + REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 1); + udelay(1); + reg_field = MS(REG_READ(ah, AR9285_AN_RF2G9), AR9285_AN_RXTXBB1_SPARE9); + REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, reg_field); + offs_6_1 = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_OFFS); + offs_0 = MS(REG_READ(ah, AR9285_AN_RF2G3), AR9285_AN_RF2G3_PDVCCOMP); + + offset = (offs_6_1<<1) | offs_0; + offset = offset - 0; + offs_6_1 = offset>>1; + offs_0 = offset & 1; + + REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, offs_6_1); + REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, offs_0); + + regVal = REG_READ(ah, 0x7834); + regVal |= 0x1; + REG_WRITE(ah, 0x7834, regVal); + regVal = REG_READ(ah, 0x9808); + regVal &= (~(0x1 << 27)); + REG_WRITE(ah, 0x9808, regVal); + + for (i = 0; i < ARRAY_SIZE(regList); i++) + REG_WRITE(ah, regList[i][0], regList[i][1]); + + REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, ccomp_org); + + if (AR_SREV_9285_11(ah)) + REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT); + +} + +bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, + u8 rxchainmask, bool longcal) +{ + bool iscaldone = true; + struct ath9k_cal_list *currCal = ah->cal_list_curr; + + if (currCal && + (currCal->calState == CAL_RUNNING || + currCal->calState == CAL_WAITING)) { + iscaldone = ath9k_hw_per_calibration(ah, chan, + rxchainmask, currCal); + if (iscaldone) { + ah->cal_list_curr = currCal = currCal->calNext; + + if (currCal->calState == CAL_WAITING) { + iscaldone = false; + ath9k_hw_reset_calibration(ah, currCal); + } + } + } + + if (longcal) { + if (AR_SREV_9285_11_OR_LATER(ah)) + ath9k_hw_9285_pa_cal(ah); + + if (OLC_FOR_AR9280_20_LATER) + ath9k_olc_temp_compensation(ah); + ath9k_hw_getnf(ah, chan); + ath9k_hw_loadnf(ah, ah->curchan); + ath9k_hw_start_nfcal(ah); + } + + return iscaldone; +} + +static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan) +{ + REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); + if (IS_CHAN_HT20(chan)) { + REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); + REG_SET_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_FLTR_CAL); + REG_CLR_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); + REG_SET_BIT(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, AH_WAIT_TIMEOUT)) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "offset " + "calibration failed to complete in " + "1ms; noisy ??\n"); + return false; + } + REG_CLR_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); + REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); + REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); + } + REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); + REG_SET_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); + REG_SET_BIT(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, AH_WAIT_TIMEOUT)) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "offset calibration " + "failed to complete in 1ms; noisy ??\n"); + return false; + } + + REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); + REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); + + return true; +} + +bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) +{ + if (AR_SREV_9285_12_OR_LATER(ah)) { + if (!ar9285_clc(ah, chan)) + return false; + } else { + if (AR_SREV_9280_10_OR_LATER(ah)) { + REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); + } + + /* Calibrate the AGC */ + REG_WRITE(ah, AR_PHY_AGC_CONTROL, + REG_READ(ah, AR_PHY_AGC_CONTROL) | + AR_PHY_AGC_CONTROL_CAL); + + /* Poll for offset calibration complete */ + if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, + 0, AH_WAIT_TIMEOUT)) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "offset calibration failed to complete in 1ms; " + "noisy environment?\n"); + return false; + } + + if (AR_SREV_9280_10_OR_LATER(ah)) { + REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); + } + } + + /* Do PA Calibration */ + if (AR_SREV_9285_11_OR_LATER(ah)) + ath9k_hw_9285_pa_cal(ah); + + /* Do NF Calibration after DC offset and other calibrations */ + REG_WRITE(ah, AR_PHY_AGC_CONTROL, + REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_NF); + + ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; + + /* Enable IQ, ADC Gain and ADC DC offset CALs */ + if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) { + if (ath9k_hw_iscal_supported(ah, ADC_GAIN_CAL)) { + INIT_CAL(&ah->adcgain_caldata); + INSERT_CAL(ah, &ah->adcgain_caldata); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "enabling ADC Gain Calibration.\n"); + } + if (ath9k_hw_iscal_supported(ah, ADC_DC_CAL)) { + INIT_CAL(&ah->adcdc_caldata); + INSERT_CAL(ah, &ah->adcdc_caldata); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "enabling ADC DC Calibration.\n"); + } + if (ath9k_hw_iscal_supported(ah, IQ_MISMATCH_CAL)) { + INIT_CAL(&ah->iq_caldata); + INSERT_CAL(ah, &ah->iq_caldata); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "enabling IQ Calibration.\n"); + } + + ah->cal_list_curr = ah->cal_list; + + if (ah->cal_list_curr) + ath9k_hw_reset_calibration(ah, ah->cal_list_curr); + } + + chan->CalValid = 0; + + return true; +} + +const struct ath9k_percal_data iq_cal_multi_sample = { + IQ_MISMATCH_CAL, + MAX_CAL_SAMPLES, + PER_MIN_LOG_COUNT, + ath9k_hw_iqcal_collect, + ath9k_hw_iqcalibrate +}; +const struct ath9k_percal_data iq_cal_single_sample = { + IQ_MISMATCH_CAL, + MIN_CAL_SAMPLES, + PER_MAX_LOG_COUNT, + ath9k_hw_iqcal_collect, + ath9k_hw_iqcalibrate +}; +const struct ath9k_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 +}; +const struct ath9k_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 +}; +const struct ath9k_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 +}; +const struct ath9k_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 +}; +const struct ath9k_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 +}; diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h new file mode 100644 index 00000000000..fe5367f1414 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2008-2009 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 CALIB_H +#define CALIB_H + +extern const struct ath9k_percal_data iq_cal_multi_sample; +extern const struct ath9k_percal_data iq_cal_single_sample; +extern const struct ath9k_percal_data adc_gain_cal_multi_sample; +extern const struct ath9k_percal_data adc_gain_cal_single_sample; +extern const struct ath9k_percal_data adc_dc_cal_multi_sample; +extern const struct ath9k_percal_data adc_dc_cal_single_sample; +extern const struct ath9k_percal_data adc_init_dc_cal; + +#define AR_PHY_CCA_MAX_GOOD_VALUE -85 +#define AR_PHY_CCA_MAX_HIGH_VALUE -62 +#define AR_PHY_CCA_MIN_BAD_VALUE -140 +#define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3 +#define AR_PHY_CCA_FILTERWINDOW_LENGTH 5 + +#define NUM_NF_READINGS 6 +#define ATH9K_NF_CAL_HIST_MAX 5 + +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)->cal_list_last == NULL) { \ + (_ahp)->cal_list = \ + (_ahp)->cal_list_last = (_perCal); \ + ((_ahp)->cal_list_last)->calNext = (_perCal); \ + } else { \ + ((_ahp)->cal_list_last)->calNext = (_perCal); \ + (_ahp)->cal_list_last = (_perCal); \ + (_perCal)->calNext = (_ahp)->cal_list; \ + } \ + } while (0) + +enum ath9k_cal_types { + ADC_DC_INIT_CAL = 0x1, + ADC_GAIN_CAL = 0x2, + ADC_DC_CAL = 0x4, + IQ_MISMATCH_CAL = 0x8 +}; + +enum ath9k_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 ath9k_percal_data { + enum ath9k_cal_types calType; + u32 calNumSamples; + u32 calCountMax; + void (*calCollect) (struct ath_hw *); + void (*calPostProc) (struct ath_hw *, u8); +}; + +struct ath9k_cal_list { + const struct ath9k_percal_data *calData; + enum ath9k_cal_state calState; + struct ath9k_cal_list *calNext; +}; + +struct ath9k_nfcal_hist { + int16_t nfCalBuffer[ATH9K_NF_CAL_HIST_MAX]; + u8 currIndex; + int16_t privNF; + u8 invalidNFcount; +}; + +bool ath9k_hw_reset_calvalid(struct ath_hw *ah); +void ath9k_hw_start_nfcal(struct ath_hw *ah); +void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan); +int16_t ath9k_hw_getnf(struct ath_hw *ah, + struct ath9k_channel *chan); +void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah); +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); +bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, + u8 rxchainmask, bool longcal); +bool ath9k_hw_init_cal(struct ath_hw *ah, + struct ath9k_channel *chan); + +#endif /* CALIB_H */ diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c new file mode 100644 index 00000000000..6d20725d645 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -0,0 +1,561 @@ +/* + * Copyright (c) 2008-2009 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 <asm/unaligned.h> + +#include "ath9k.h" + +static unsigned int ath9k_debug = DBG_DEFAULT; +module_param_named(debug, ath9k_debug, uint, 0); + +static struct dentry *ath9k_debugfs_root; + +void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...) +{ + if (!sc) + return; + + if (sc->debug.debug_mask & dbg_mask) { + va_list args; + + va_start(args, fmt); + printk(KERN_DEBUG "ath9k: "); + vprintk(fmt, args); + va_end(args); + } +} + +static int ath9k_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t read_file_debug(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = snprintf(buf, sizeof(buf), "0x%08x\n", sc->debug.debug_mask); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_debug(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long mask; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EINVAL; + + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &mask)) + return -EINVAL; + + sc->debug.debug_mask = mask; + return count; +} + +static const struct file_operations fops_debug = { + .read = read_file_debug, + .write = write_file_debug, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + +static ssize_t read_file_dma(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + char buf[1024]; + unsigned int len = 0; + u32 val[ATH9K_NUM_DMA_DEBUG_REGS]; + int i, qcuOffset = 0, dcuOffset = 0; + u32 *qcuBase = &val[0], *dcuBase = &val[4]; + + 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))); + + len += snprintf(buf + len, sizeof(buf) - len, + "Raw DMA Debug values:\n"); + + for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) { + if (i % 4 == 0) + len += snprintf(buf + len, sizeof(buf) - len, "\n"); + + val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32))); + len += snprintf(buf + len, sizeof(buf) - len, "%d: %08x ", + i, val[i]); + } + + len += snprintf(buf + len, sizeof(buf) - len, "\n\n"); + len += snprintf(buf + len, sizeof(buf) - len, + "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++; + } + + len += snprintf(buf + len, sizeof(buf) - len, + "%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); + } + + len += snprintf(buf + len, sizeof(buf) - len, "\n"); + + len += snprintf(buf + len, sizeof(buf) - len, + "qcu_stitch state: %2x qcu_fetch state: %2x\n", + (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22); + len += snprintf(buf + len, sizeof(buf) - len, + "qcu_complete state: %2x dcu_complete state: %2x\n", + (val[3] & 0x1c000000) >> 26, (val[6] & 0x3)); + len += snprintf(buf + len, sizeof(buf) - len, + "dcu_arb state: %2x dcu_fp state: %2x\n", + (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27); + len += snprintf(buf + len, sizeof(buf) - len, + "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n", + (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10); + len += snprintf(buf + len, sizeof(buf) - len, + "txfifo_valid_0: %1d txfifo_valid_1: %1d\n", + (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12); + len += snprintf(buf + len, sizeof(buf) - len, + "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n", + (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); + + len += snprintf(buf + len, sizeof(buf) - len, "pcu observe: 0x%x \n", + REG_READ(ah, AR_OBS_BUS_1)); + len += snprintf(buf + len, sizeof(buf) - len, + "AR_CR: 0x%x \n", REG_READ(ah, AR_CR)); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_dma = { + .read = read_file_dma, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + + +void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) +{ + if (status) + sc->debug.stats.istats.total++; + if (status & ATH9K_INT_RX) + sc->debug.stats.istats.rxok++; + if (status & ATH9K_INT_RXEOL) + sc->debug.stats.istats.rxeol++; + if (status & ATH9K_INT_RXORN) + sc->debug.stats.istats.rxorn++; + if (status & ATH9K_INT_TX) + sc->debug.stats.istats.txok++; + if (status & ATH9K_INT_TXURN) + sc->debug.stats.istats.txurn++; + if (status & ATH9K_INT_MIB) + sc->debug.stats.istats.mib++; + if (status & ATH9K_INT_RXPHY) + sc->debug.stats.istats.rxphyerr++; + if (status & ATH9K_INT_RXKCM) + sc->debug.stats.istats.rx_keycache_miss++; + if (status & ATH9K_INT_SWBA) + sc->debug.stats.istats.swba++; + if (status & ATH9K_INT_BMISS) + sc->debug.stats.istats.bmiss++; + if (status & ATH9K_INT_BNR) + sc->debug.stats.istats.bnr++; + if (status & ATH9K_INT_CST) + sc->debug.stats.istats.cst++; + if (status & ATH9K_INT_GTT) + sc->debug.stats.istats.gtt++; + if (status & ATH9K_INT_TIM) + sc->debug.stats.istats.tim++; + if (status & ATH9K_INT_CABEND) + sc->debug.stats.istats.cabend++; + if (status & ATH9K_INT_DTIMSYNC) + sc->debug.stats.istats.dtimsync++; + if (status & ATH9K_INT_DTIM) + sc->debug.stats.istats.dtim++; +} + +static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[512]; + unsigned int len = 0; + + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "RX", sc->debug.stats.istats.rxok); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "RXEOL", sc->debug.stats.istats.rxeol); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "RXORN", sc->debug.stats.istats.rxorn); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "TX", sc->debug.stats.istats.txok); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "TXURN", sc->debug.stats.istats.txurn); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "MIB", sc->debug.stats.istats.mib); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "RXPHY", sc->debug.stats.istats.rxphyerr); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "RXKCM", sc->debug.stats.istats.rx_keycache_miss); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "SWBA", sc->debug.stats.istats.swba); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "BMISS", sc->debug.stats.istats.bmiss); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "BNR", sc->debug.stats.istats.bnr); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "CST", sc->debug.stats.istats.cst); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "GTT", sc->debug.stats.istats.gtt); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "TIM", sc->debug.stats.istats.tim); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "CABEND", sc->debug.stats.istats.cabend); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "DTIMSYNC", sc->debug.stats.istats.dtimsync); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "DTIM", sc->debug.stats.istats.dtim); + len += snprintf(buf + len, sizeof(buf) - len, + "%8s: %10u\n", "TOTAL", sc->debug.stats.istats.total); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_interrupt = { + .read = read_file_interrupt, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + +void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb) +{ + struct ath_tx_info_priv *tx_info_priv = NULL; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *rates = tx_info->status.rates; + int final_ts_idx, idx; + struct ath_rc_stats *stats; + + tx_info_priv = ATH_TX_INFO_PRIV(tx_info); + final_ts_idx = tx_info_priv->tx.ts_rateindex; + idx = rates[final_ts_idx].idx; + stats = &sc->debug.stats.rcstats[idx]; + stats->success++; +} + +void ath_debug_stat_retries(struct ath_softc *sc, int rix, + int xretries, int retries, u8 per) +{ + struct ath_rc_stats *stats = &sc->debug.stats.rcstats[rix]; + + stats->xretries += xretries; + stats->retries += retries; + stats->per = per; +} + +static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char *buf; + unsigned int len = 0, max; + int i = 0; + ssize_t retval; + + if (sc->cur_rate_table == NULL) + return 0; + + max = 80 + sc->cur_rate_table->rate_cnt * 64; + buf = kmalloc(max + 1, GFP_KERNEL); + if (buf == NULL) + return 0; + buf[max] = 0; + + len += sprintf(buf, "%5s %15s %8s %9s %3s\n\n", "Rate", "Success", + "Retries", "XRetries", "PER"); + + for (i = 0; i < sc->cur_rate_table->rate_cnt; i++) { + u32 ratekbps = sc->cur_rate_table->info[i].ratekbps; + struct ath_rc_stats *stats = &sc->debug.stats.rcstats[i]; + + len += snprintf(buf + len, max - len, + "%3u.%d: %8u %8u %8u %8u\n", ratekbps / 1000, + (ratekbps % 1000) / 100, stats->success, + stats->retries, stats->xretries, + stats->per); + } + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return retval; +} + +static const struct file_operations fops_rcstat = { + .read = read_file_rcstat, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + +static const char * ath_wiphy_state_str(enum ath_wiphy_state state) +{ + switch (state) { + case ATH_WIPHY_INACTIVE: + return "INACTIVE"; + case ATH_WIPHY_ACTIVE: + return "ACTIVE"; + case ATH_WIPHY_PAUSING: + return "PAUSING"; + case ATH_WIPHY_PAUSED: + return "PAUSED"; + case ATH_WIPHY_SCAN: + return "SCAN"; + } + return "?"; +} + +static ssize_t read_file_wiphy(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[512]; + unsigned int len = 0; + int i; + u8 addr[ETH_ALEN]; + + len += snprintf(buf + len, sizeof(buf) - len, + "primary: %s (%s chan=%d ht=%d)\n", + wiphy_name(sc->pri_wiphy->hw->wiphy), + ath_wiphy_state_str(sc->pri_wiphy->state), + sc->pri_wiphy->chan_idx, sc->pri_wiphy->chan_is_ht); + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + if (aphy == NULL) + continue; + len += snprintf(buf + len, sizeof(buf) - len, + "secondary: %s (%s chan=%d ht=%d)\n", + wiphy_name(aphy->hw->wiphy), + ath_wiphy_state_str(aphy->state), + aphy->chan_idx, aphy->chan_is_ht); + } + + put_unaligned_le32(REG_READ(sc->sc_ah, AR_STA_ID0), addr); + put_unaligned_le16(REG_READ(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4); + len += snprintf(buf + len, sizeof(buf) - len, + "addr: %pM\n", addr); + put_unaligned_le32(REG_READ(sc->sc_ah, AR_BSSMSKL), addr); + put_unaligned_le16(REG_READ(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4); + len += snprintf(buf + len, sizeof(buf) - len, + "addrmask: %pM\n", addr); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static struct ath_wiphy * get_wiphy(struct ath_softc *sc, const char *name) +{ + int i; + if (strcmp(name, wiphy_name(sc->pri_wiphy->hw->wiphy)) == 0) + return sc->pri_wiphy; + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + if (aphy && strcmp(name, wiphy_name(aphy->hw->wiphy)) == 0) + return aphy; + } + return NULL; +} + +static int del_wiphy(struct ath_softc *sc, const char *name) +{ + struct ath_wiphy *aphy = get_wiphy(sc, name); + if (!aphy) + return -ENOENT; + return ath9k_wiphy_del(aphy); +} + +static int pause_wiphy(struct ath_softc *sc, const char *name) +{ + struct ath_wiphy *aphy = get_wiphy(sc, name); + if (!aphy) + return -ENOENT; + return ath9k_wiphy_pause(aphy); +} + +static int unpause_wiphy(struct ath_softc *sc, const char *name) +{ + struct ath_wiphy *aphy = get_wiphy(sc, name); + if (!aphy) + return -ENOENT; + return ath9k_wiphy_unpause(aphy); +} + +static int select_wiphy(struct ath_softc *sc, const char *name) +{ + struct ath_wiphy *aphy = get_wiphy(sc, name); + if (!aphy) + return -ENOENT; + return ath9k_wiphy_select(aphy); +} + +static int schedule_wiphy(struct ath_softc *sc, const char *msec) +{ + ath9k_wiphy_set_scheduler(sc, simple_strtoul(msec, NULL, 0)); + return 0; +} + +static ssize_t write_file_wiphy(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[50]; + size_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + + if (strncmp(buf, "add", 3) == 0) { + int res = ath9k_wiphy_add(sc); + if (res < 0) + return res; + } else if (strncmp(buf, "del=", 4) == 0) { + int res = del_wiphy(sc, buf + 4); + if (res < 0) + return res; + } else if (strncmp(buf, "pause=", 6) == 0) { + int res = pause_wiphy(sc, buf + 6); + if (res < 0) + return res; + } else if (strncmp(buf, "unpause=", 8) == 0) { + int res = unpause_wiphy(sc, buf + 8); + if (res < 0) + return res; + } else if (strncmp(buf, "select=", 7) == 0) { + int res = select_wiphy(sc, buf + 7); + if (res < 0) + return res; + } else if (strncmp(buf, "schedule=", 9) == 0) { + int res = schedule_wiphy(sc, buf + 9); + if (res < 0) + return res; + } else + return -EOPNOTSUPP; + + return count; +} + +static const struct file_operations fops_wiphy = { + .read = read_file_wiphy, + .write = write_file_wiphy, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + + +int ath9k_init_debug(struct ath_softc *sc) +{ + sc->debug.debug_mask = ath9k_debug; + + if (!ath9k_debugfs_root) + return -ENOENT; + + sc->debug.debugfs_phy = debugfs_create_dir(wiphy_name(sc->hw->wiphy), + ath9k_debugfs_root); + if (!sc->debug.debugfs_phy) + goto err; + + sc->debug.debugfs_debug = debugfs_create_file("debug", + S_IRUGO | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_debug); + if (!sc->debug.debugfs_debug) + goto err; + + sc->debug.debugfs_dma = debugfs_create_file("dma", S_IRUGO, + sc->debug.debugfs_phy, sc, &fops_dma); + if (!sc->debug.debugfs_dma) + goto err; + + sc->debug.debugfs_interrupt = debugfs_create_file("interrupt", + S_IRUGO, + sc->debug.debugfs_phy, + sc, &fops_interrupt); + if (!sc->debug.debugfs_interrupt) + goto err; + + sc->debug.debugfs_rcstat = debugfs_create_file("rcstat", + S_IRUGO, + sc->debug.debugfs_phy, + sc, &fops_rcstat); + if (!sc->debug.debugfs_rcstat) + goto err; + + sc->debug.debugfs_wiphy = debugfs_create_file( + "wiphy", S_IRUGO | S_IWUSR, sc->debug.debugfs_phy, sc, + &fops_wiphy); + if (!sc->debug.debugfs_wiphy) + goto err; + + return 0; +err: + ath9k_exit_debug(sc); + return -ENOMEM; +} + +void ath9k_exit_debug(struct ath_softc *sc) +{ + debugfs_remove(sc->debug.debugfs_wiphy); + debugfs_remove(sc->debug.debugfs_rcstat); + debugfs_remove(sc->debug.debugfs_interrupt); + debugfs_remove(sc->debug.debugfs_dma); + debugfs_remove(sc->debug.debugfs_debug); + debugfs_remove(sc->debug.debugfs_phy); +} + +int ath9k_debug_create_root(void) +{ + ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!ath9k_debugfs_root) + return -ENOENT; + + return 0; +} + +void ath9k_debug_remove_root(void) +{ + debugfs_remove(ath9k_debugfs_root); + ath9k_debugfs_root = NULL; +} diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h new file mode 100644 index 00000000000..edda15bf2c1 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2008-2009 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 DEBUG_H +#define DEBUG_H + +enum ATH_DEBUG { + ATH_DBG_RESET = 0x00000001, + ATH_DBG_QUEUE = 0x00000002, + ATH_DBG_EEPROM = 0x00000004, + ATH_DBG_CALIBRATE = 0x00000008, + ATH_DBG_INTERRUPT = 0x00000010, + ATH_DBG_REGULATORY = 0x00000020, + ATH_DBG_ANI = 0x00000040, + ATH_DBG_XMIT = 0x00000080, + ATH_DBG_BEACON = 0x00000100, + ATH_DBG_CONFIG = 0x00000200, + ATH_DBG_FATAL = 0x00000400, + ATH_DBG_PS = 0x00000800, + ATH_DBG_ANY = 0xffffffff +}; + +#define DBG_DEFAULT (ATH_DBG_FATAL) + +#ifdef CONFIG_ATH9K_DEBUG + +/** + * struct ath_interrupt_stats - Contains statistics about interrupts + * @total: Total no. of interrupts generated so far + * @rxok: RX with no errors + * @rxeol: RX with no more RXDESC available + * @rxorn: RX FIFO overrun + * @txok: TX completed at the requested rate + * @txurn: TX FIFO underrun + * @mib: MIB regs reaching its threshold + * @rxphyerr: RX with phy errors + * @rx_keycache_miss: RX with key cache misses + * @swba: Software Beacon Alert + * @bmiss: Beacon Miss + * @bnr: Beacon Not Ready + * @cst: Carrier Sense TImeout + * @gtt: Global TX Timeout + * @tim: RX beacon TIM occurrence + * @cabend: RX End of CAB traffic + * @dtimsync: DTIM sync lossage + * @dtim: RX Beacon with DTIM + */ +struct ath_interrupt_stats { + u32 total; + u32 rxok; + u32 rxeol; + u32 rxorn; + u32 txok; + u32 txeol; + u32 txurn; + u32 mib; + u32 rxphyerr; + u32 rx_keycache_miss; + u32 swba; + u32 bmiss; + u32 bnr; + u32 cst; + u32 gtt; + u32 tim; + u32 cabend; + u32 dtimsync; + u32 dtim; +}; + +struct ath_rc_stats { + u32 success; + u32 retries; + u32 xretries; + u8 per; +}; + +struct ath_stats { + struct ath_interrupt_stats istats; + struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; +}; + +struct ath9k_debug { + int debug_mask; + struct dentry *debugfs_phy; + struct dentry *debugfs_debug; + struct dentry *debugfs_dma; + struct dentry *debugfs_interrupt; + struct dentry *debugfs_rcstat; + struct dentry *debugfs_wiphy; + struct ath_stats stats; +}; + +void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...); +int ath9k_init_debug(struct ath_softc *sc); +void ath9k_exit_debug(struct ath_softc *sc); +int ath9k_debug_create_root(void); +void ath9k_debug_remove_root(void); +void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status); +void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb); +void ath_debug_stat_retries(struct ath_softc *sc, int rix, + int xretries, int retries, u8 per); + +#else + +static inline void DPRINTF(struct ath_softc *sc, int dbg_mask, + const char *fmt, ...) +{ +} + +static inline int ath9k_init_debug(struct ath_softc *sc) +{ + return 0; +} + +static inline void ath9k_exit_debug(struct ath_softc *sc) +{ +} + +static inline int ath9k_debug_create_root(void) +{ + return 0; +} + +static inline void ath9k_debug_remove_root(void) +{ +} + +static inline void ath_debug_stat_interrupt(struct ath_softc *sc, + enum ath9k_int status) +{ +} + +static inline void ath_debug_stat_rc(struct ath_softc *sc, + struct sk_buff *skb) +{ +} + +static inline void ath_debug_stat_retries(struct ath_softc *sc, int rix, + int xretries, int retries, u8 per) +{ +} + +#endif /* CONFIG_ATH9K_DEBUG */ + +#endif /* DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c new file mode 100644 index 00000000000..a2fda702b62 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -0,0 +1,2786 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +static void ath9k_hw_analog_shift_rmw(struct ath_hw *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->config.analog_shiftreg) + udelay(100); + + return; +} + +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 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 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 inline bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) +{ + struct ath_softc *sc = ah->ah_sc; + + return sc->bus_ops->eeprom_read(ah, off, data); +} + +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 void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, + struct ath9k_channel *chan, + struct cal_target_power_leg *powInfo, + u16 numChannels, + struct cal_target_power_leg *pNewPower, + u16 numRates, bool isExtTarget) +{ + struct chan_centers centers; + u16 clo, chi; + int i; + int matchIndex = -1, lowIndex = -1; + u16 freq; + + 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 void ath9k_get_txgain_index(struct ath_hw *ah, + struct ath9k_channel *chan, + struct calDataPerFreqOpLoop *rawDatasetOpLoop, + u8 *calChans, u16 availPiers, u8 *pwr, u8 *pcdacIdx) +{ + u8 pcdac, i = 0; + u16 idxL = 0, idxR = 0, numPiers; + bool match; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + for (numPiers = 0; numPiers < availPiers; numPiers++) + if (calChans[numPiers] == AR5416_BCHAN_UNUSED) + break; + + match = ath9k_hw_get_lower_upper_index( + (u8)FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan)), + calChans, numPiers, &idxL, &idxR); + if (match) { + pcdac = rawDatasetOpLoop[idxL].pcdac[0][0]; + *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0]; + } else { + pcdac = rawDatasetOpLoop[idxR].pcdac[0][0]; + *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] + + rawDatasetOpLoop[idxR].pwrPdg[0][0])/2; + } + + while (pcdac > ah->originalGain[i] && + i < (AR9280_TX_GAIN_TABLE_SIZE - 1)) + i++; + + *pcdacIdx = i; + return; +} + +static void ath9k_olc_get_pdadcs(struct ath_hw *ah, + u32 initTxGain, + int txPower, + u8 *pPDADCValues) +{ + u32 i; + u32 offset; + + REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, + AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); + REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, + AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); + + REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, + AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain); + + offset = txPower; + for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++) + if (i < offset) + pPDADCValues[i] = 0x0; + else + pPDADCValues[i] = 0xFF; +} + + + + +static void ath9k_hw_get_target_powers(struct ath_hw *ah, + struct ath9k_channel *chan, + struct cal_target_power_ht *powInfo, + u16 numChannels, + struct cal_target_power_ht *pNewPower, + u16 numRates, bool isHt40Target) +{ + struct chan_centers centers; + u16 clo, chi; + int i; + int matchIndex = -1, lowIndex = -1; + u16 freq; + + 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 u16 ath9k_hw_get_max_edge_power(u16 freq, + struct cal_ctl_edges *pRdEdgesPower, + bool is2GHz, int num_band_edges) +{ + u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + int i; + + for (i = 0; (i < 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; +} + +/****************************************/ +/* EEPROM Operations for 4K sized cards */ +/****************************************/ + +static int ath9k_hw_4k_get_eeprom_ver(struct ath_hw *ah) +{ + return ((ah->eeprom.map4k.baseEepHeader.version >> 12) & 0xF); +} + +static int ath9k_hw_4k_get_eeprom_rev(struct ath_hw *ah) +{ + return ((ah->eeprom.map4k.baseEepHeader.version) & 0xFFF); +} + +static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) +{ +#define SIZE_EEPROM_4K (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) + u16 *eep_data = (u16 *)&ah->eeprom.map4k; + int addr, eep_start_loc = 0; + + eep_start_loc = 64; + + if (!ath9k_hw_use_flash(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Reading from EEPROM, not flash\n"); + } + + for (addr = 0; addr < SIZE_EEPROM_4K; addr++) { + if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Unable to read eeprom region \n"); + return false; + } + eep_data++; + } + + return true; +#undef SIZE_EEPROM_4K +} + +static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) +{ +#define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) + struct ar5416_eeprom_4k *eep = + (struct ar5416_eeprom_4k *) &ah->eeprom.map4k; + u16 *eepdata, temp, magic, magic2; + u32 sum = 0, el; + bool need_swap = false; + int i, addr; + + + if (!ath9k_hw_use_flash(ah)) { + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, + &magic)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Reading Magic # failed\n"); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); + + if (magic != AR5416_EEPROM_MAGIC) { + magic2 = swab16(magic); + + if (magic2 == AR5416_EEPROM_MAGIC) { + need_swap = true; + eepdata = (u16 *) (&ah->eeprom); + + for (addr = 0; addr < EEPROM_4K_SIZE; addr++) { + temp = swab16(*eepdata); + *eepdata = temp; + eepdata++; + } + } else { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Invalid EEPROM Magic. " + "endianness mismatch.\n"); + return -EINVAL; + } + } + } + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", + need_swap ? "True" : "False"); + + if (need_swap) + el = swab16(ah->eeprom.map4k.baseEepHeader.length); + else + el = ah->eeprom.map4k.baseEepHeader.length; + + if (el > sizeof(struct ar5416_eeprom_4k)) + el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16); + else + el = el / sizeof(u16); + + eepdata = (u16 *)(&ah->eeprom); + + for (i = 0; i < el; i++) + sum ^= *eepdata++; + + if (need_swap) { + u32 integer; + 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; + + integer = swab32(eep->modalHeader.antCtrlCommon); + eep->modalHeader.antCtrlCommon = integer; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + integer = swab32(eep->modalHeader.antCtrlChain[i]); + eep->modalHeader.antCtrlChain[i] = integer; + } + + for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) { + word = swab16(eep->modalHeader.spurChans[i].spurChan); + eep->modalHeader.spurChans[i].spurChan = word; + } + } + + if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || + ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Bad EEPROM checksum 0x%x or revision 0x%04x\n", + sum, ah->eep_ops->get_eeprom_ver(ah)); + return -EINVAL; + } + + return 0; +#undef EEPROM_4K_SIZE +} + +static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, + enum eeprom_param param) +{ + struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; + struct modal_eep_4k_header *pModal = &eep->modalHeader; + struct base_eep_header_4k *pBase = &eep->baseEepHeader; + + switch (param) { + case EEP_NFTHRESH_2: + return pModal->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_2: + return pModal->ob_01; + case EEP_DB_2: + return pModal->db1_01; + 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; + case EEP_FRAC_N_5G: + return 0; + default: + return 0; + } +} + +static void ath9k_hw_get_4k_gain_boundaries_pdadcs(struct ath_hw *ah, + struct ath9k_channel *chan, + struct cal_data_per_freq_4k *pRawDataSet, + u8 *bChans, u16 availPiers, + u16 tPdGainOverlap, int16_t *pMinCalPower, + u16 *pPdGainBoundaries, u8 *pPDADCValues, + u16 numXpdGains) +{ +#define TMP_VAL_VPD_TABLE \ + ((vpdTableI[i][sizeCurrVpdTable - 1] + (ss - maxIndex + 1) * vpdStep)); + int i, j, k; + int16_t ss; + u16 idxL = 0, idxR = 0, numPiers; + static u8 vpdTableL[AR5416_EEP4K_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + static u8 vpdTableR[AR5416_EEP4K_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + static u8 vpdTableI[AR5416_EEP4K_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR; + u8 minPwrT4[AR5416_EEP4K_NUM_PD_GAINS]; + u8 maxPwrT4[AR5416_EEP4K_NUM_PD_GAINS]; + int16_t vpdStep; + int16_t tmpVal; + u16 sizeCurrVpdTable, maxIndex, tgtIndex; + bool match; + int16_t minDelta = 0; + struct chan_centers centers; +#define PD_GAIN_BOUNDARY_DEFAULT 58; + + 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_EEP4K_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_EEP4K_PD_GAIN_ICEPTS - 1], + pPwrR[AR5416_EEP4K_PD_GAIN_ICEPTS - 1]); + + + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pPwrL, pVpdL, + AR5416_EEP4K_PD_GAIN_ICEPTS, + vpdTableL[i]); + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pPwrR, pVpdR, + AR5416_EEP4K_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_20_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) TMP_VAL_VPD_TABLE; + pPDADCValues[k++] = (u8)((tmpVal > 255) ? + 255 : tmpVal); + ss++; + } + } + } + + while (i < AR5416_EEP4K_PD_GAINS_IN_MASK) { + pPdGainBoundaries[i] = PD_GAIN_BOUNDARY_DEFAULT; + i++; + } + + while (k < AR5416_NUM_PDADC_VALUES) { + pPDADCValues[k] = pPDADCValues[k - 1]; + k++; + } + + return; +#undef TMP_VAL_VPD_TABLE +} + +static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, + struct ath9k_channel *chan, + int16_t *pTxPowerIndexOffset) +{ + struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; + struct cal_data_per_freq_4k *pRawDataset; + u8 *pCalBChans = NULL; + u16 pdGainOverlap_t2; + static u8 pdadcValues[AR5416_NUM_PDADC_VALUES]; + u16 gainBoundaries[AR5416_EEP4K_PD_GAINS_IN_MASK]; + u16 numPiers, i, j; + int16_t tMinCalPower; + u16 numXpdGain, xpdMask; + u16 xpdGainValues[AR5416_EEP4K_NUM_PD_GAINS] = { 0, 0 }; + u32 reg32, regOffset, regChainOffset; + + xpdMask = pEepData->modalHeader.xpdGain; + + if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + pdGainOverlap_t2 = + pEepData->modalHeader.pdGainOverlap; + } else { + pdGainOverlap_t2 = (u16)(MS(REG_READ(ah, AR_PHY_TPCRG5), + AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); + } + + pCalBChans = pEepData->calFreqPier2G; + numPiers = AR5416_EEP4K_NUM_2G_CAL_PIERS; + + numXpdGain = 0; + + for (i = 1; i <= AR5416_EEP4K_PD_GAINS_IN_MASK; i++) { + if ((xpdMask >> (AR5416_EEP4K_PD_GAINS_IN_MASK - i)) & 1) { + if (numXpdGain >= AR5416_EEP4K_NUM_PD_GAINS) + break; + xpdGainValues[numXpdGain] = + (u16)(AR5416_EEP4K_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, 0); + + for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) { + if (AR_SREV_5416_20_OR_LATER(ah) && + (ah->rxchainmask == 5 || ah->txchainmask == 5) && + (i != 0)) { + regChainOffset = (i == 1) ? 0x2000 : 0x1000; + } else + regChainOffset = i * 0x1000; + + if (pEepData->baseEepHeader.txMask & (1 << i)) { + pRawDataset = pEepData->calPierData2G[i]; + + ath9k_hw_get_4k_gain_boundaries_pdadcs(ah, chan, + pRawDataset, pCalBChans, + numPiers, pdGainOverlap_t2, + &tMinCalPower, gainBoundaries, + pdadcValues, numXpdGain); + + if ((i == 0) || AR_SREV_5416_20_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_EEPROM, + "PDADC (%d,%4x): %4.4x %8.8x\n", + i, regChainOffset, regOffset, + reg32); + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "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; +} + +static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah, + struct ath9k_channel *chan, + int16_t *ratesArray, + u16 cfgCtl, + u16 AntennaReduction, + u16 twiceMaxRegulatoryPower, + u16 powerLimit) +{ + struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; + u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + static const u16 tpScaleReductionTable[5] = + { 0, 3, 6, 9, AR5416_MAX_RATE_POWER }; + + int i; + int16_t twiceLargestAntenna; + struct cal_ctl_data_4k *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} + }; + u16 scaledPower = 0, minCtlPower, maxRegAllowedPower; + 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; + u16 twiceMinEdgePower; + + tx_chainmask = ah->txchainmask; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + twiceLargestAntenna = pEepData->modalHeader.antennaGainCh[0]; + + twiceLargestAntenna = (int16_t)min(AntennaReduction - + twiceLargestAntenna, 0); + + maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna; + + if (ah->regulatory.tp_scale != ATH9K_TP_SCALE_MAX) { + maxRegAllowedPower -= + (tpScaleReductionTable[(ah->regulatory.tp_scale)] * 2); + } + + scaledPower = min(powerLimit, maxRegAllowedPower); + scaledPower = max((u16)0, scaledPower); + + 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); + } + + 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 (ah->eep_ops->get_eeprom_ver(ah) == 14 && + ah->eep_ops->get_eeprom_rev(ah) <= 2) + twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "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_EEPROM, + " 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), + AR5416_EEP4K_NUM_BAND_EDGES); + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + " 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 = (u8)min(twiceMaxEdgePower, scaledPower); + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + " 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((u16)targetPowerCck.tPow2x[i], + minCtlPower); + } + break; + case CTL_11G: + for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); + i++) { + targetPowerOfdm.tPow2x[i] = + min((u16)targetPowerOfdm.tPow2x[i], + minCtlPower); + } + break; + case CTL_2GHT20: + for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); + i++) { + targetPowerHt20.tPow2x[i] = + min((u16)targetPowerHt20.tPow2x[i], + minCtlPower); + } + break; + case CTL_11B_EXT: + targetPowerCckExt.tPow2x[0] = min((u16) + targetPowerCckExt.tPow2x[0], + minCtlPower); + break; + case CTL_11G_EXT: + targetPowerOfdmExt.tPow2x[0] = min((u16) + targetPowerOfdmExt.tPow2x[0], + minCtlPower); + break; + case CTL_2GHT40: + for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); + i++) { + targetPowerHt40.tPow2x[i] = + min((u16)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]; + + 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]; + ratesArray[rateExtCck] = targetPowerCckExt.tPow2x[0]; + } +} + +static void ath9k_hw_4k_set_txpower(struct ath_hw *ah, + struct ath9k_channel *chan, + u16 cfgCtl, + u8 twiceAntennaReduction, + u8 twiceMaxRegulatoryPower, + u8 powerLimit) +{ + struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; + struct modal_eep_4k_header *pModal = &pEepData->modalHeader; + 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; + } + + ath9k_hw_set_4k_power_per_rate_table(ah, chan, + &ratesArray[0], cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, + powerLimit); + + ath9k_hw_set_4k_power_cal_table(ah, chan, &txPowerIndexOffset); + + 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)); + } + + 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->regulatory.max_power_level = + ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2; + else + ah->regulatory.max_power_level = ratesArray[i]; + +} + +static void ath9k_hw_4k_set_addac(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_4k_header *pModal; + struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; + u8 biaslevel; + + if (ah->hw_version.macVersion != AR_SREV_VERSION_9160) + return; + + if (ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_MINOR_VER_7) + return; + + pModal = &eep->modalHeader; + + if (pModal->xpaBiasLvl != 0xff) { + biaslevel = pModal->xpaBiasLvl; + INI_RA(&ah->iniAddac, 7, 1) = + (INI_RA(&ah->iniAddac, 7, 1) & (~0x18)) | biaslevel << 3; + } +} + +static void ath9k_hw_4k_set_gain(struct ath_hw *ah, + struct modal_eep_4k_header *pModal, + struct ar5416_eeprom_4k *eep, + u8 txRxAttenLocal, int regChainOffset) +{ + REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, + pModal->antCtrlChain[0]); + + 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[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | + SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); + + if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_3) { + txRxAttenLocal = pModal->txRxAttenCh[0]; + + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, + pModal->xatten2Margin[0]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]); + } + + 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[0]); + + if (AR_SREV_9285_11(ah)) + REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14)); +} + +static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_4k_header *pModal; + struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; + u8 txRxAttenLocal; + u8 ob[5], db1[5], db2[5]; + u8 ant_div_control1, ant_div_control2; + u32 regVal; + + pModal = &eep->modalHeader; + txRxAttenLocal = 23; + + REG_WRITE(ah, AR_PHY_SWITCH_COM, + ah->eep_ops->get_eeprom_antenna_cfg(ah, chan)); + + /* Single chain for 4K EEPROM*/ + ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal, 0); + + /* Initialize Ant Diversity settings from EEPROM */ + if (pModal->version == 3) { + ant_div_control1 = ((pModal->ob_234 >> 12) & 0xf); + ant_div_control2 = ((pModal->db1_234 >> 12) & 0xf); + regVal = REG_READ(ah, 0x99ac); + regVal &= (~(0x7f000000)); + regVal |= ((ant_div_control1 & 0x1) << 24); + regVal |= (((ant_div_control1 >> 1) & 0x1) << 29); + regVal |= (((ant_div_control1 >> 2) & 0x1) << 30); + regVal |= ((ant_div_control2 & 0x3) << 25); + regVal |= (((ant_div_control2 >> 2) & 0x3) << 27); + REG_WRITE(ah, 0x99ac, regVal); + regVal = REG_READ(ah, 0x99ac); + regVal = REG_READ(ah, 0xa208); + regVal &= (~(0x1 << 13)); + regVal |= (((ant_div_control1 >> 3) & 0x1) << 13); + REG_WRITE(ah, 0xa208, regVal); + regVal = REG_READ(ah, 0xa208); + } + + if (pModal->version >= 2) { + ob[0] = (pModal->ob_01 & 0xf); + ob[1] = (pModal->ob_01 >> 4) & 0xf; + ob[2] = (pModal->ob_234 & 0xf); + ob[3] = ((pModal->ob_234 >> 4) & 0xf); + ob[4] = ((pModal->ob_234 >> 8) & 0xf); + + db1[0] = (pModal->db1_01 & 0xf); + db1[1] = ((pModal->db1_01 >> 4) & 0xf); + db1[2] = (pModal->db1_234 & 0xf); + db1[3] = ((pModal->db1_234 >> 4) & 0xf); + db1[4] = ((pModal->db1_234 >> 8) & 0xf); + + db2[0] = (pModal->db2_01 & 0xf); + db2[1] = ((pModal->db2_01 >> 4) & 0xf); + db2[2] = (pModal->db2_234 & 0xf); + db2[3] = ((pModal->db2_234 >> 4) & 0xf); + db2[4] = ((pModal->db2_234 >> 8) & 0xf); + + } else if (pModal->version == 1) { + ob[0] = (pModal->ob_01 & 0xf); + ob[1] = ob[2] = ob[3] = ob[4] = (pModal->ob_01 >> 4) & 0xf; + db1[0] = (pModal->db1_01 & 0xf); + db1[1] = db1[2] = db1[3] = + db1[4] = ((pModal->db1_01 >> 4) & 0xf); + db2[0] = (pModal->db2_01 & 0xf); + db2[1] = db2[2] = db2[3] = + db2[4] = ((pModal->db2_01 >> 4) & 0xf); + } else { + int i; + for (i = 0; i < 5; i++) { + ob[i] = pModal->ob_01; + db1[i] = pModal->db1_01; + db2[i] = pModal->db1_01; + } + } + + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, + AR9285_AN_RF2G3_OB_0, AR9285_AN_RF2G3_OB_0_S, ob[0]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, + AR9285_AN_RF2G3_OB_1, AR9285_AN_RF2G3_OB_1_S, ob[1]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, + AR9285_AN_RF2G3_OB_2, AR9285_AN_RF2G3_OB_2_S, ob[2]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, + AR9285_AN_RF2G3_OB_3, AR9285_AN_RF2G3_OB_3_S, ob[3]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, + AR9285_AN_RF2G3_OB_4, AR9285_AN_RF2G3_OB_4_S, ob[4]); + + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, + AR9285_AN_RF2G3_DB1_0, AR9285_AN_RF2G3_DB1_0_S, db1[0]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, + AR9285_AN_RF2G3_DB1_1, AR9285_AN_RF2G3_DB1_1_S, db1[1]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, + AR9285_AN_RF2G3_DB1_2, AR9285_AN_RF2G3_DB1_2_S, db1[2]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB1_3, AR9285_AN_RF2G4_DB1_3_S, db1[3]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB1_4, AR9285_AN_RF2G4_DB1_4_S, db1[4]); + + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB2_0, AR9285_AN_RF2G4_DB2_0_S, db2[0]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB2_1, AR9285_AN_RF2G4_DB2_1_S, db2[1]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB2_2, AR9285_AN_RF2G4_DB2_2_S, db2[2]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB2_3, AR9285_AN_RF2G4_DB2_3_S, db2[3]); + ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB2_4, AR9285_AN_RF2G4_DB2_4_S, db2[4]); + + + if (AR_SREV_9285_11(ah)) + REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT); + + 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); + + 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); + 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); + + 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); + } +} + +static u16 ath9k_hw_4k_get_eeprom_antenna_cfg(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; + struct modal_eep_4k_header *pModal = &eep->modalHeader; + + return pModal->antCtrlCommon & 0xFFFF; +} + +static u8 ath9k_hw_4k_get_num_ant_config(struct ath_hw *ah, + enum ieee80211_band freq_band) +{ + return 1; +} + +static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) +{ +#define EEP_MAP4K_SPURCHAN \ + (ah->eeprom.map4k.modalHeader.spurChans[i].spurChan) + + 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->config.spurchans[i][is2GHz]); + + switch (ah->config.spurmode) { + case SPUR_DISABLE: + break; + case SPUR_ENABLE_IOCTL: + spur_val = ah->config.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_MAP4K_SPURCHAN; + break; + } + + return spur_val; + +#undef EEP_MAP4K_SPURCHAN +} + +static struct eeprom_ops eep_4k_ops = { + .check_eeprom = ath9k_hw_4k_check_eeprom, + .get_eeprom = ath9k_hw_4k_get_eeprom, + .fill_eeprom = ath9k_hw_4k_fill_eeprom, + .get_eeprom_ver = ath9k_hw_4k_get_eeprom_ver, + .get_eeprom_rev = ath9k_hw_4k_get_eeprom_rev, + .get_num_ant_config = ath9k_hw_4k_get_num_ant_config, + .get_eeprom_antenna_cfg = ath9k_hw_4k_get_eeprom_antenna_cfg, + .set_board_values = ath9k_hw_4k_set_board_values, + .set_addac = ath9k_hw_4k_set_addac, + .set_txpower = ath9k_hw_4k_set_txpower, + .get_spur_channel = ath9k_hw_4k_get_spur_channel +}; + +/************************************************/ +/* EEPROM Operations for non-4K (Default) cards */ +/************************************************/ + +static int ath9k_hw_def_get_eeprom_ver(struct ath_hw *ah) +{ + return ((ah->eeprom.def.baseEepHeader.version >> 12) & 0xF); +} + +static int ath9k_hw_def_get_eeprom_rev(struct ath_hw *ah) +{ + return ((ah->eeprom.def.baseEepHeader.version) & 0xFFF); +} + +static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah) +{ +#define SIZE_EEPROM_DEF (sizeof(struct ar5416_eeprom_def) / sizeof(u16)) + u16 *eep_data = (u16 *)&ah->eeprom.def; + int addr, ar5416_eep_start_loc = 0x100; + + for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) { + if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc, + eep_data)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Unable to read eeprom region\n"); + return false; + } + eep_data++; + } + return true; +#undef SIZE_EEPROM_DEF +} + +static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) +{ + struct ar5416_eeprom_def *eep = + (struct ar5416_eeprom_def *) &ah->eeprom.def; + u16 *eepdata, temp, magic, magic2; + u32 sum = 0, el; + bool need_swap = false; + int i, addr, size; + + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Reading Magic # failed\n"); + return false; + } + + if (!ath9k_hw_use_flash(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); + + if (magic != AR5416_EEPROM_MAGIC) { + magic2 = swab16(magic); + + if (magic2 == AR5416_EEPROM_MAGIC) { + size = sizeof(struct ar5416_eeprom_def); + need_swap = true; + eepdata = (u16 *) (&ah->eeprom); + + for (addr = 0; addr < size / sizeof(u16); addr++) { + temp = swab16(*eepdata); + *eepdata = temp; + eepdata++; + } + } else { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Invalid EEPROM Magic. " + "Endianness mismatch.\n"); + return -EINVAL; + } + } + } + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", + need_swap ? "True" : "False"); + + if (need_swap) + el = swab16(ah->eeprom.def.baseEepHeader.length); + else + el = ah->eeprom.def.baseEepHeader.length; + + if (el > sizeof(struct ar5416_eeprom_def)) + el = sizeof(struct ar5416_eeprom_def) / sizeof(u16); + else + el = el / sizeof(u16); + + eepdata = (u16 *)(&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 || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || + ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Bad EEPROM checksum 0x%x or revision 0x%04x\n", + sum, ah->eep_ops->get_eeprom_ver(ah)); + return -EINVAL; + } + + return 0; +} + +static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, + enum eeprom_param param) +{ + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + 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 AR5416_VER_MASK; + case EEP_TX_MASK: + return pBase->txMask; + case EEP_RX_MASK: + return pBase->rxMask; + case EEP_RXGAIN_TYPE: + return pBase->rxGainType; + case EEP_TXGAIN_TYPE: + return pBase->txGainType; + case EEP_OL_PWRCTRL: + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) + return pBase->openLoopPwrCntl ? true : false; + else + return false; + case EEP_RC_CHAIN_MASK: + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) + return pBase->rcChainMask; + else + return 0; + case EEP_DAC_HPWR_5G: + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) + return pBase->dacHiPwrMode_5G; + else + return 0; + case EEP_FRAC_N_5G: + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_22) + return pBase->frac_n_5g; + else + return 0; + default: + return 0; + } +} + +static void ath9k_hw_def_set_gain(struct ath_hw *ah, + struct modal_eep_header *pModal, + struct ar5416_eeprom_def *eep, + u8 txRxAttenLocal, int regChainOffset, int i) +{ + if (AR5416_VER_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)); + } +} + +static void ath9k_hw_def_set_board_values(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_header *pModal; + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + int i, regChainOffset; + u8 txRxAttenLocal; + + pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); + txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44; + + REG_WRITE(ah, AR_PHY_SWITCH_COM, + ah->eep_ops->get_eeprom_antenna_cfg(ah, chan)); + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + if (AR_SREV_9280(ah)) { + if (i >= 2) + break; + } + + if (AR_SREV_5416_20_OR_LATER(ah) && + (ah->rxchainmask == 5 || 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_20_OR_LATER(ah)) + ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal, + regChainOffset, i); + } + + 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); + 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 (AR5416_VER_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 (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) { + if (IS_CHAN_HT40(chan)) + REG_RMW_FIELD(ah, AR_PHY_SETTLING, + AR_PHY_SETTLING_SWITCH, + pModal->swSettleHt40); + } + + if (AR_SREV_9280_20_OR_LATER(ah) && + AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) + REG_RMW_FIELD(ah, AR_PHY_CCK_TX_CTRL, + AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK, + pModal->miscBits); + + + if (AR_SREV_9280_20(ah) && AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) { + if (IS_CHAN_2GHZ(chan)) + REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, + eep->baseEepHeader.dacLpMode); + else if (eep->baseEepHeader.dacHiPwrMode_5G) + REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, 0); + else + REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, + eep->baseEepHeader.dacLpMode); + + REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, + pModal->miscBits >> 2); + + REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL9, + AR_PHY_TX_DESIRED_SCALE_CCK, + eep->baseEepHeader.desiredScaleCCK); + } +} + +static void ath9k_hw_def_set_addac(struct ath_hw *ah, + struct ath9k_channel *chan) +{ +#define XPA_LVL_FREQ(cnt) (pModal->xpaBiasLvlFreq[cnt]) + struct modal_eep_header *pModal; + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + u8 biaslevel; + + if (ah->hw_version.macVersion != AR_SREV_VERSION_9160) + return; + + if (ah->eep_ops->get_eeprom_rev(ah) < 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 = XPA_LVL_FREQ(0) & 0xff; + biaslevel = (u8) (XPA_LVL_FREQ(0) >> 14); + + freqCount++; + + while (freqCount < 3) { + if (XPA_LVL_FREQ(freqCount) == 0x0) + break; + + freqBin = XPA_LVL_FREQ(freqCount) & 0xff; + if (resetFreqBin >= freqBin) + biaslevel = (u8)(XPA_LVL_FREQ(freqCount) >> 14); + else + break; + freqCount++; + } + } + + if (IS_CHAN_2GHZ(chan)) { + INI_RA(&ah->iniAddac, 7, 1) = (INI_RA(&ah->iniAddac, + 7, 1) & (~0x18)) | biaslevel << 3; + } else { + INI_RA(&ah->iniAddac, 6, 1) = (INI_RA(&ah->iniAddac, + 6, 1) & (~0xc0)) | biaslevel << 6; + } +#undef XPA_LVL_FREQ +} + +static void ath9k_hw_get_def_gain_boundaries_pdadcs(struct ath_hw *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_20_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 void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, + struct ath9k_channel *chan, + int16_t *pTxPowerIndexOffset) +{ +#define SM_PD_GAIN(x) SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##x) +#define SM_PDGAIN_B(x, y) \ + SM((gainBoundaries[x]), AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##y) + + struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; + 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; + + 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; + } + + if (OLC_FOR_AR9280_20_LATER && IS_CHAN_2GHZ(chan)) { + pRawDataset = pEepData->calPierData2G[0]; + ah->initPDADC = ((struct calDataPerFreqOpLoop *) + pRawDataset)->vpdPdg[0][0]; + } + + 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_20_OR_LATER(ah) && + (ah->rxchainmask == 5 || 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]; + + + if (OLC_FOR_AR9280_20_LATER) { + u8 pcdacIdx; + u8 txPower; + + ath9k_get_txgain_index(ah, chan, + (struct calDataPerFreqOpLoop *)pRawDataset, + pCalBChans, numPiers, &txPower, &pcdacIdx); + ath9k_olc_get_pdadcs(ah, pcdacIdx, + txPower/2, pdadcValues); + } else { + ath9k_hw_get_def_gain_boundaries_pdadcs(ah, + chan, pRawDataset, + pCalBChans, numPiers, + pdGainOverlap_t2, + &tMinCalPower, + gainBoundaries, + pdadcValues, + numXpdGain); + } + + if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) { + if (OLC_FOR_AR9280_20_LATER) { + REG_WRITE(ah, + AR_PHY_TPCRG5 + regChainOffset, + SM(0x6, + AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | + SM_PD_GAIN(1) | SM_PD_GAIN(2) | + SM_PD_GAIN(3) | SM_PD_GAIN(4)); + } else { + REG_WRITE(ah, + AR_PHY_TPCRG5 + regChainOffset, + SM(pdGainOverlap_t2, + AR_PHY_TPCRG5_PD_GAIN_OVERLAP)| + SM_PDGAIN_B(0, 1) | + SM_PDGAIN_B(1, 2) | + SM_PDGAIN_B(2, 3) | + SM_PDGAIN_B(3, 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_EEPROM, + "PDADC (%d,%4x): %4.4x %8.8x\n", + i, regChainOffset, regOffset, + reg32); + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "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; +#undef SM_PD_GAIN +#undef SM_PDGAIN_B +} + +static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, + struct ath9k_channel *chan, + int16_t *ratesArray, + u16 cfgCtl, + u16 AntennaReduction, + u16 twiceMaxRegulatoryPower, + u16 powerLimit) +{ +#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ +#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */ + + struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; + u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + static const u16 tpScaleReductionTable[5] = + { 0, 3, 6, 9, AR5416_MAX_RATE_POWER }; + + int i; + int16_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} + }; + u16 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; + u16 twiceMinEdgePower; + + tx_chainmask = 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 = (int16_t)min(AntennaReduction - + twiceLargestAntenna, 0); + + maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna; + + if (ah->regulatory.tp_scale != ATH9K_TP_SCALE_MAX) { + maxRegAllowedPower -= + (tpScaleReductionTable[(ah->regulatory.tp_scale)] * 2); + } + + scaledPower = min(powerLimit, maxRegAllowedPower); + + switch (ar5416_get_ntxchains(tx_chainmask)) { + case 1: + break; + case 2: + scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN; + break; + case 3: + scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN; + break; + } + + scaledPower = max((u16)0, 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 (ah->eep_ops->get_eeprom_ver(ah) == 14 && + ah->eep_ops->get_eeprom_rev(ah) <= 2) + twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "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_EEPROM, + " 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), AR5416_NUM_BAND_EDGES); + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + " 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_EEPROM, + " 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((u16)targetPowerCck.tPow2x[i], + minCtlPower); + } + break; + case CTL_11A: + case CTL_11G: + for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); i++) { + targetPowerOfdm.tPow2x[i] = + min((u16)targetPowerOfdm.tPow2x[i], + minCtlPower); + } + break; + case CTL_5GHT20: + case CTL_2GHT20: + for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) { + targetPowerHt20.tPow2x[i] = + min((u16)targetPowerHt20.tPow2x[i], + minCtlPower); + } + break; + case CTL_11B_EXT: + targetPowerCckExt.tPow2x[0] = min((u16) + targetPowerCckExt.tPow2x[0], + minCtlPower); + break; + case CTL_11A_EXT: + case CTL_11G_EXT: + targetPowerOfdmExt.tPow2x[0] = min((u16) + targetPowerOfdmExt.tPow2x[0], + minCtlPower); + break; + case CTL_5GHT40: + case CTL_2GHT40: + for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) { + targetPowerHt40.tPow2x[i] = + min((u16)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]; + } + } +} + +static void ath9k_hw_def_set_txpower(struct ath_hw *ah, + struct ath9k_channel *chan, + u16 cfgCtl, + u8 twiceAntennaReduction, + u8 twiceMaxRegulatoryPower, + u8 powerLimit) +{ +#define RT_AR_DELTA(x) (ratesArray[x] - cck_ofdm_delta) + struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; + struct modal_eep_header *pModal = + &(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]); + int16_t ratesArray[Ar5416RateSize]; + int16_t txPowerIndexOffset = 0; + u8 ht40PowerIncForPdadc = 2; + int i, cck_ofdm_delta = 0; + + memset(ratesArray, 0, sizeof(ratesArray)); + + if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; + } + + ath9k_hw_set_def_power_per_rate_table(ah, chan, + &ratesArray[0], cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, + powerLimit); + + ath9k_hw_set_def_power_cal_table(ah, chan, &txPowerIndexOffset); + + 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)) { + if (OLC_FOR_AR9280_20_LATER) { + cck_ofdm_delta = 2; + REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, + ATH9K_POW_SM(RT_AR_DELTA(rate2s), 24) + | ATH9K_POW_SM(RT_AR_DELTA(rate2l), 16) + | ATH9K_POW_SM(ratesArray[rateXr], 8) + | ATH9K_POW_SM(RT_AR_DELTA(rate1l), 0)); + REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, + ATH9K_POW_SM(RT_AR_DELTA(rate11s), 24) + | ATH9K_POW_SM(RT_AR_DELTA(rate11l), 16) + | ATH9K_POW_SM(RT_AR_DELTA(rate5_5s), 8) + | ATH9K_POW_SM(RT_AR_DELTA(rate5_5l), 0)); + } else { + 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)); + if (OLC_FOR_AR9280_20_LATER) { + REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, + ATH9K_POW_SM(ratesArray[rateExtOfdm], 24) + | ATH9K_POW_SM(RT_AR_DELTA(rateExtCck), 16) + | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8) + | ATH9K_POW_SM(RT_AR_DELTA(rateDupCck), 0)); + } else { + 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->regulatory.max_power_level = + ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2; + else + ah->regulatory.max_power_level = ratesArray[i]; + + switch(ar5416_get_ntxchains(ah->txchainmask)) { + case 1: + break; + case 2: + ah->regulatory.max_power_level += INCREASE_MAXPOW_BY_TWO_CHAIN; + break; + case 3: + ah->regulatory.max_power_level += INCREASE_MAXPOW_BY_THREE_CHAIN; + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Invalid chainmask configuration\n"); + break; + } +} + +static u8 ath9k_hw_def_get_num_ant_config(struct ath_hw *ah, + enum ieee80211_band freq_band) +{ + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + struct modal_eep_header *pModal = + &(eep->modalHeader[ATH9K_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 u16 ath9k_hw_def_get_eeprom_antenna_cfg(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + struct modal_eep_header *pModal = + &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); + + return pModal->antCtrlCommon & 0xFFFF; +} + +static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) +{ +#define EEP_DEF_SPURCHAN \ + (ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan) + + 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->config.spurchans[i][is2GHz]); + + switch (ah->config.spurmode) { + case SPUR_DISABLE: + break; + case SPUR_ENABLE_IOCTL: + spur_val = ah->config.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_DEF_SPURCHAN; + break; + } + + return spur_val; + +#undef EEP_DEF_SPURCHAN +} + +static struct eeprom_ops eep_def_ops = { + .check_eeprom = ath9k_hw_def_check_eeprom, + .get_eeprom = ath9k_hw_def_get_eeprom, + .fill_eeprom = ath9k_hw_def_fill_eeprom, + .get_eeprom_ver = ath9k_hw_def_get_eeprom_ver, + .get_eeprom_rev = ath9k_hw_def_get_eeprom_rev, + .get_num_ant_config = ath9k_hw_def_get_num_ant_config, + .get_eeprom_antenna_cfg = ath9k_hw_def_get_eeprom_antenna_cfg, + .set_board_values = ath9k_hw_def_set_board_values, + .set_addac = ath9k_hw_def_set_addac, + .set_txpower = ath9k_hw_def_set_txpower, + .get_spur_channel = ath9k_hw_def_get_spur_channel +}; + +int ath9k_hw_eeprom_attach(struct ath_hw *ah) +{ + int status; + + if (AR_SREV_9285(ah)) { + ah->eep_map = EEP_MAP_4KBITS; + ah->eep_ops = &eep_4k_ops; + } else { + ah->eep_map = EEP_MAP_DEFAULT; + ah->eep_ops = &eep_def_ops; + } + + if (!ah->eep_ops->fill_eeprom(ah)) + return -EIO; + + status = ah->eep_ops->check_eeprom(ah); + + return status; +} diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h new file mode 100644 index 00000000000..67b8bd12941 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2008-2009 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 EEPROM_H +#define EEPROM_H + +#include <net/cfg80211.h> + +#define AH_USE_EEPROM 0x1 + +#ifdef __BIG_ENDIAN +#define AR5416_EEPROM_MAGIC 0x5aa5 +#else +#define AR5416_EEPROM_MAGIC 0xa55a +#endif + +#define CTRY_DEBUG 0x1ff +#define CTRY_DEFAULT 0 + +#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 AR5416_EEPROM_MAGIC_OFFSET 0x0 +#define AR5416_EEPROM_S 2 +#define AR5416_EEPROM_OFFSET 0x2000 +#define AR5416_EEPROM_MAX 0xae0 + +#define AR5416_EEPROM_START_ADDR \ + (AR_SREV_9100(ah)) ? 0x1fff1000 : 0x503f1200 + +#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 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 INCREASE_MAXPOW_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ +#define INCREASE_MAXPOW_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */ + +/* + * For AR9285 and later chipsets, the following bits are not being programmed + * in EEPROM and so need to be enabled always. + * + * Bit 0: en_fcc_mid + * Bit 1: en_jap_mid + * Bit 2: en_fcc_dfs_ht40 + * Bit 3: en_jap_ht40 + * Bit 4: en_jap_dfs_ht40 + */ +#define AR9285_RDEXT_DEFAULT 0x1F + +#define AR_EEPROM_MAC(i) (0x1d+(i)) +#define ATH9K_POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) +#define FREQ2FBIN(x, y) ((y) ? ((x) - 2300) : (((x) - 4800) / 5)) +#define ath9k_hw_use_flash(_ah) (!(_ah->ah_flags & AH_USE_EEPROM)) + +#define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) +#define OLC_FOR_AR9280_20_LATER (AR_SREV_9280_20_OR_LATER(ah) && \ + ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) + +#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 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_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 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_MINOR_VER_16 0x10 +#define AR5416_EEP_MINOR_VER_17 0x11 +#define AR5416_EEP_MINOR_VER_19 0x13 +#define AR5416_EEP_MINOR_VER_20 0x14 +#define AR5416_EEP_MINOR_VER_22 0x16 + +#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_BCHAN_UNUSED 0xFF +#define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64 +#define AR5416_MAX_CHAINS 3 +#define AR5416_PWR_TABLE_OFFSET -5 + +/* Rx gain type values */ +#define AR5416_EEP_RXGAIN_23DB_BACKOFF 0 +#define AR5416_EEP_RXGAIN_13DB_BACKOFF 1 +#define AR5416_EEP_RXGAIN_ORIG 2 + +/* Tx gain type values */ +#define AR5416_EEP_TXGAIN_ORIGINAL 0 +#define AR5416_EEP_TXGAIN_HIGH_POWER 1 + +#define AR5416_EEP4K_START_LOC 64 +#define AR5416_EEP4K_NUM_2G_CAL_PIERS 3 +#define AR5416_EEP4K_NUM_2G_CCK_TARGET_POWERS 3 +#define AR5416_EEP4K_NUM_2G_20_TARGET_POWERS 3 +#define AR5416_EEP4K_NUM_2G_40_TARGET_POWERS 3 +#define AR5416_EEP4K_NUM_CTLS 12 +#define AR5416_EEP4K_NUM_BAND_EDGES 4 +#define AR5416_EEP4K_NUM_PD_GAINS 2 +#define AR5416_EEP4K_PD_GAINS_IN_MASK 4 +#define AR5416_EEP4K_PD_GAIN_ICEPTS 5 +#define AR5416_EEP4K_MAX_CHAINS 1 + +#define AR9280_TX_GAIN_TABLE_SIZE 22 + +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, + EEP_RXGAIN_TYPE, + EEP_TXGAIN_TYPE, + EEP_OL_PWRCTRL, + EEP_RC_CHAIN_MASK, + EEP_DAC_HPWR_5G, + EEP_FRAC_N_5G +}; + +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 +}; + +enum ath9k_hal_freq_band { + ATH9K_HAL_FREQ_BAND_5GHZ = 0, + ATH9K_HAL_FREQ_BAND_2GHZ = 1 +}; + +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_1[2]; + u8 rxGainType; + u8 dacHiPwrMode_5G; + u8 openLoopPwrCntl; + u8 dacLpMode; + u8 txGainType; + u8 rcChainMask; + u8 desiredScaleCCK; + u8 power_table_offset; + u8 frac_n_5g; + u8 futureBase_3[21]; +} __packed; + +struct base_eep_header_4k { + 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 txGainType; +} __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 miscBits; + u16 xpaBiasLvlFreq[3]; + u8 futureModal[6]; + + struct spur_chan spurChans[AR5416_EEPROM_MODAL_SPURS]; +} __packed; + +struct calDataPerFreqOpLoop { + u8 pwrPdg[2][5]; + u8 vpdPdg[2][5]; + u8 pcdac[2][5]; + u8 empty[2][5]; +} __packed; + +struct modal_eep_4k_header { + u32 antCtrlChain[AR5416_EEP4K_MAX_CHAINS]; + u32 antCtrlCommon; + u8 antennaGainCh[AR5416_EEP4K_MAX_CHAINS]; + u8 switchSettling; + u8 txRxAttenCh[AR5416_EEP4K_MAX_CHAINS]; + u8 rxTxMarginCh[AR5416_EEP4K_MAX_CHAINS]; + u8 adcDesiredSize; + u8 pgaDesiredSize; + u8 xlnaGainCh[AR5416_EEP4K_MAX_CHAINS]; + u8 txEndToXpaOff; + u8 txEndToRxOn; + u8 txFrameToXpaOn; + u8 thresh62; + u8 noiseFloorThreshCh[AR5416_EEP4K_MAX_CHAINS]; + u8 xpdGain; + u8 xpd; + u8 iqCalICh[AR5416_EEP4K_MAX_CHAINS]; + u8 iqCalQCh[AR5416_EEP4K_MAX_CHAINS]; + u8 pdGainOverlap; + u8 ob_01; + u8 db1_01; + u8 xpaBiasLvl; + u8 txFrameToDataStart; + u8 txFrameToPaOn; + u8 ht40PowerIncForPdadc; + u8 bswAtten[AR5416_EEP4K_MAX_CHAINS]; + u8 bswMargin[AR5416_EEP4K_MAX_CHAINS]; + u8 swSettleHt40; + u8 xatten2Db[AR5416_EEP4K_MAX_CHAINS]; + u8 xatten2Margin[AR5416_EEP4K_MAX_CHAINS]; + u8 db2_01; + u8 version; + u16 ob_234; + u16 db1_234; + u16 db2_234; + u8 futureModal[4]; + + 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_data_per_freq_4k { + u8 pwrPdg[AR5416_EEP4K_NUM_PD_GAINS][AR5416_EEP4K_PD_GAIN_ICEPTS]; + u8 vpdPdg[AR5416_EEP4K_NUM_PD_GAINS][AR5416_EEP4K_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 cal_ctl_data_4k { + struct cal_ctl_edges + ctlEdges[AR5416_EEP4K_MAX_CHAINS][AR5416_EEP4K_NUM_BAND_EDGES]; +} __packed; + +struct ar5416_eeprom_def { + 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 ar5416_eeprom_4k { + struct base_eep_header_4k baseEepHeader; + u8 custData[20]; + struct modal_eep_4k_header modalHeader; + u8 calFreqPier2G[AR5416_EEP4K_NUM_2G_CAL_PIERS]; + struct cal_data_per_freq_4k + calPierData2G[AR5416_EEP4K_MAX_CHAINS][AR5416_EEP4K_NUM_2G_CAL_PIERS]; + struct cal_target_power_leg + calTargetPowerCck[AR5416_EEP4K_NUM_2G_CCK_TARGET_POWERS]; + struct cal_target_power_leg + calTargetPower2G[AR5416_EEP4K_NUM_2G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower2GHT20[AR5416_EEP4K_NUM_2G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower2GHT40[AR5416_EEP4K_NUM_2G_40_TARGET_POWERS]; + u8 ctlIndex[AR5416_EEP4K_NUM_CTLS]; + struct cal_ctl_data_4k ctlData[AR5416_EEP4K_NUM_CTLS]; + u8 padding; +} __packed; + +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]; +}; + +enum ath9k_eep_map { + EEP_MAP_DEFAULT = 0x0, + EEP_MAP_4KBITS, + EEP_MAP_MAX +}; + +struct eeprom_ops { + int (*check_eeprom)(struct ath_hw *hw); + u32 (*get_eeprom)(struct ath_hw *hw, enum eeprom_param param); + bool (*fill_eeprom)(struct ath_hw *hw); + int (*get_eeprom_ver)(struct ath_hw *hw); + int (*get_eeprom_rev)(struct ath_hw *hw); + u8 (*get_num_ant_config)(struct ath_hw *hw, enum ieee80211_band band); + u16 (*get_eeprom_antenna_cfg)(struct ath_hw *hw, + struct ath9k_channel *chan); + void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan); + void (*set_addac)(struct ath_hw *hw, struct ath9k_channel *chan); + void (*set_txpower)(struct ath_hw *hw, struct ath9k_channel *chan, + u16 cfgCtl, u8 twiceAntennaReduction, + u8 twiceMaxRegulatoryPower, u8 powerLimit); + u16 (*get_spur_channel)(struct ath_hw *ah, u16 i, bool is2GHz); +}; + +#define ar5416_get_ntxchains(_txchainmask) \ + (((_txchainmask >> 2) & 1) + \ + ((_txchainmask >> 1) & 1) + (_txchainmask & 1)) + +int ath9k_hw_eeprom_attach(struct ath_hw *ah); + +#endif /* EEPROM_H */ diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c new file mode 100644 index 00000000000..1579c9407ed --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -0,0 +1,3873 @@ +/* + * Copyright (c) 2008-2009 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 <linux/io.h> +#include <asm/unaligned.h> + +#include "ath9k.h" +#include "initvals.h" + +static int btcoex_enable; +module_param(btcoex_enable, bool, 0); +MODULE_PARM_DESC(btcoex_enable, "Enable Bluetooth coexistence support"); + +#define ATH9K_CLOCK_RATE_CCK 22 +#define ATH9K_CLOCK_RATE_5GHZ_OFDM 40 +#define ATH9K_CLOCK_RATE_2GHZ_OFDM 44 + +static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type); +static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode); +static u32 ath9k_hw_ini_fixup(struct ath_hw *ah, + struct ar5416_eeprom_def *pEepData, + u32 reg, u32 value); +static void ath9k_hw_9280_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan); +static void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan); + +/********************/ +/* Helper Functions */ +/********************/ + +static u32 ath9k_hw_mac_usec(struct ath_hw *ah, u32 clks) +{ + struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + + if (!ah->curchan) /* should really check for CCK instead */ + return clks / ATH9K_CLOCK_RATE_CCK; + if (conf->channel->band == IEEE80211_BAND_2GHZ) + return clks / ATH9K_CLOCK_RATE_2GHZ_OFDM; + + return clks / ATH9K_CLOCK_RATE_5GHZ_OFDM; +} + +static u32 ath9k_hw_mac_to_usec(struct ath_hw *ah, u32 clks) +{ + struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + + if (conf_is_ht40(conf)) + return ath9k_hw_mac_usec(ah, clks) / 2; + else + return ath9k_hw_mac_usec(ah, clks); +} + +static u32 ath9k_hw_mac_clks(struct ath_hw *ah, u32 usecs) +{ + struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + + if (!ah->curchan) /* should really check for CCK instead */ + return usecs *ATH9K_CLOCK_RATE_CCK; + if (conf->channel->band == IEEE80211_BAND_2GHZ) + return usecs *ATH9K_CLOCK_RATE_2GHZ_OFDM; + return usecs *ATH9K_CLOCK_RATE_5GHZ_OFDM; +} + +static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs) +{ + struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + + if (conf_is_ht40(conf)) + return ath9k_hw_mac_clks(ah, usecs) * 2; + else + return ath9k_hw_mac_clks(ah, usecs); +} + +/* + * Read and write, they both share the same lock. We do this to serialize + * reads and writes on Atheros 802.11n PCI devices only. This is required + * as the FIFO on these devices can only accept sanely 2 requests. After + * that the device goes bananas. Serializing the reads/writes prevents this + * from happening. + */ + +void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val) +{ + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { + unsigned long flags; + spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags); + iowrite32(val, ah->ah_sc->mem + reg_offset); + spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags); + } else + iowrite32(val, ah->ah_sc->mem + reg_offset); +} + +unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset) +{ + u32 val; + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { + unsigned long flags; + spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags); + val = ioread32(ah->ah_sc->mem + reg_offset); + spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags); + } else + val = ioread32(ah->ah_sc->mem + reg_offset); + return val; +} + +bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout) +{ + int i; + + BUG_ON(timeout < AH_TIME_QUANTUM); + + for (i = 0; i < (timeout / AH_TIME_QUANTUM); i++) { + if ((REG_READ(ah, reg) & mask) == val) + return true; + + udelay(AH_TIME_QUANTUM); + } + + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "timeout (%d us) on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n", + timeout, reg, REG_READ(ah, reg), mask, val); + + return false; +} + +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; +} + +bool ath9k_get_channel_edges(struct ath_hw *ah, + u16 flags, u16 *low, + u16 *high) +{ + struct ath9k_hw_capabilities *pCap = &ah->caps; + + if (flags & CHANNEL_5GHZ) { + *low = pCap->low_5ghz_chan; + *high = pCap->high_5ghz_chan; + return true; + } + if ((flags & CHANNEL_2GHZ)) { + *low = pCap->low_2ghz_chan; + *high = pCap->high_2ghz_chan; + return true; + } + return false; +} + +u16 ath9k_hw_computetxtime(struct ath_hw *ah, + const struct ath_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 WLAN_RC_PHY_CCK: + phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; + if (shortPreamble && rates->info[rateix].short_preamble) + phyTime >>= 1; + numBits = frameLen << 3; + txTime = CCK_SIFS_TIME + phyTime + ((numBits * 1000) / kbps); + break; + case WLAN_RC_PHY_OFDM: + if (ah->curchan && IS_CHAN_QUARTER_RATE(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->curchan && + IS_CHAN_HALF_RATE(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_FATAL, + "Unknown phy %u (rate ix %u)\n", + rates->info[rateix].phy, rateix); + txTime = 0; + break; + } + + return txTime; +} + +void ath9k_hw_get_channel_centers(struct ath_hw *ah, + struct ath9k_channel *chan, + struct chan_centers *centers) +{ + int8_t extoff; + + 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 * + ((ah->extprotspacing == ATH9K_HT_EXTPROTSPACING_20) ? + HT40_CHANNEL_CENTER_SHIFT : 15)); +} + +/******************/ +/* Chip Revisions */ +/******************/ + +static void ath9k_hw_read_revisions(struct ath_hw *ah) +{ + u32 val; + + val = REG_READ(ah, AR_SREV) & AR_SREV_ID; + + if (val == 0xFF) { + val = REG_READ(ah, AR_SREV); + ah->hw_version.macVersion = + (val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S; + ah->hw_version.macRev = MS(val, AR_SREV_REVISION2); + ah->is_pciexpress = (val & AR_SREV_TYPE2_HOST_MODE) ? 0 : 1; + } else { + if (!AR_SREV_9100(ah)) + ah->hw_version.macVersion = MS(val, AR_SREV_VERSION); + + ah->hw_version.macRev = val & AR_SREV_REVISION; + + if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE) + ah->is_pciexpress = true; + } +} + +static int ath9k_hw_get_radiorev(struct ath_hw *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); +} + +/************************************/ +/* HW Attach, Detach, Init Routines */ +/************************************/ + +static void ath9k_hw_disablepcie(struct ath_hw *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 bool ath9k_hw_chip_test(struct ath_hw *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_FATAL, + "address test failed " + "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", + 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_FATAL, + "address test failed " + "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", + addr, wrData, rdData); + return false; + } + } + REG_WRITE(ah, regAddr[i], regHold[i]); + } + udelay(100); + + return true; +} + +static const char *ath9k_hw_devname(u16 devid) +{ + switch (devid) { + case AR5416_DEVID_PCI: + return "Atheros 5416"; + case AR5416_DEVID_PCIE: + return "Atheros 5418"; + case AR9160_DEVID_PCI: + return "Atheros 9160"; + case AR5416_AR9100_DEVID: + return "Atheros 9100"; + case AR9280_DEVID_PCI: + case AR9280_DEVID_PCIE: + return "Atheros 9280"; + case AR9285_DEVID_PCIE: + return "Atheros 9285"; + } + + return NULL; +} + +static void ath9k_hw_set_defaults(struct ath_hw *ah) +{ + int i; + + ah->config.dma_beacon_response_time = 2; + ah->config.sw_beacon_response_time = 10; + ah->config.additional_swba_backoff = 0; + ah->config.ack_6mb = 0x0; + ah->config.cwm_ignore_extcca = 0; + ah->config.pcie_powersave_enable = 0; + ah->config.pcie_clock_req = 0; + ah->config.pcie_waen = 0; + ah->config.analog_shiftreg = 1; + ah->config.ht_enable = 1; + ah->config.ofdm_trig_low = 200; + ah->config.ofdm_trig_high = 500; + ah->config.cck_trig_high = 200; + ah->config.cck_trig_low = 100; + ah->config.enable_ani = 1; + ah->config.diversity_control = 0; + ah->config.antenna_switch_swap = 0; + + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + ah->config.spurchans[i][0] = AR_NO_SPUR; + ah->config.spurchans[i][1] = AR_NO_SPUR; + } + + ah->config.intr_mitigation = true; + + /* + * We need this for PCI devices only (Cardbus, PCI, miniPCI) + * _and_ if on non-uniprocessor systems (Multiprocessor/HT). + * This means we use it for all AR5416 devices, and the few + * minor PCI AR9280 devices out there. + * + * Serialization is required because these devices do not handle + * well the case of two concurrent reads/writes due to the latency + * involved. During one read/write another read/write can be issued + * on another CPU while the previous read/write may still be working + * on our hardware, if we hit this case the hardware poops in a loop. + * We prevent this by serializing reads and writes. + * + * This issue is not present on PCI-Express devices or pre-AR5416 + * devices (legacy, 802.11abg). + */ + if (num_possible_cpus() > 1) + ah->config.serialize_regmode = SER_REG_MODE_AUTO; +} + +static struct ath_hw *ath9k_hw_newstate(u16 devid, struct ath_softc *sc, + int *status) +{ + struct ath_hw *ah; + + ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); + if (ah == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "Cannot allocate memory for state block\n"); + *status = -ENOMEM; + return NULL; + } + + ah->ah_sc = sc; + ah->hw_version.magic = AR5416_MAGIC; + ah->regulatory.country_code = CTRY_DEFAULT; + ah->hw_version.devid = devid; + ah->hw_version.subvendorid = 0; + + ah->ah_flags = 0; + if ((devid == AR5416_AR9100_DEVID)) + ah->hw_version.macVersion = AR_SREV_VERSION_9100; + if (!AR_SREV_9100(ah)) + ah->ah_flags = AH_USE_EEPROM; + + ah->regulatory.power_limit = MAX_RATE_POWER; + ah->regulatory.tp_scale = ATH9K_TP_SCALE_MAX; + ah->atim_window = 0; + ah->diversity_control = ah->config.diversity_control; + ah->antenna_switch_swap = + ah->config.antenna_switch_swap; + ah->sta_id1_defaults = AR_STA_ID1_CRPT_MIC_ENABLE; + ah->beacon_interval = 100; + ah->enable_32kHz_clock = DONT_USE_32KHZ; + ah->slottime = (u32) -1; + ah->acktimeout = (u32) -1; + ah->ctstimeout = (u32) -1; + ah->globaltxtimeout = (u32) -1; + + ah->gbeacon_rate = 0; + + return ah; +} + +static int ath9k_hw_rfattach(struct ath_hw *ah) +{ + bool rfStatus = false; + int ecode = 0; + + rfStatus = ath9k_hw_init_rf(ah, &ecode); + if (!rfStatus) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "RF setup failed, status: %u\n", ecode); + return ecode; + } + + return 0; +} + +static int ath9k_hw_rf_claim(struct ath_hw *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_FATAL, + "Radio Chip Rev 0x%02X not supported\n", + val & AR_RADIO_SREV_MAJOR); + return -EOPNOTSUPP; + } + + ah->hw_version.analog5GhzRev = val; + + return 0; +} + +static int ath9k_hw_init_macaddr(struct ath_hw *ah) +{ + u32 sum; + int i; + u16 eeval; + + sum = 0; + for (i = 0; i < 3; i++) { + eeval = ah->eep_ops->get_eeprom(ah, AR_EEPROM_MAC(i)); + sum += eeval; + ah->macaddr[2 * i] = eeval >> 8; + ah->macaddr[2 * i + 1] = eeval & 0xff; + } + if (sum == 0 || sum == 0xffff * 3) + return -EADDRNOTAVAIL; + + return 0; +} + +static void ath9k_hw_init_rxgain_ini(struct ath_hw *ah) +{ + u32 rxgain_type; + + if (ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV) >= AR5416_EEP_MINOR_VER_17) { + rxgain_type = ah->eep_ops->get_eeprom(ah, EEP_RXGAIN_TYPE); + + if (rxgain_type == AR5416_EEP_RXGAIN_13DB_BACKOFF) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9280Modes_backoff_13db_rxgain_9280_2, + ARRAY_SIZE(ar9280Modes_backoff_13db_rxgain_9280_2), 6); + else if (rxgain_type == AR5416_EEP_RXGAIN_23DB_BACKOFF) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9280Modes_backoff_23db_rxgain_9280_2, + ARRAY_SIZE(ar9280Modes_backoff_23db_rxgain_9280_2), 6); + else + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9280Modes_original_rxgain_9280_2, + ARRAY_SIZE(ar9280Modes_original_rxgain_9280_2), 6); + } else { + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9280Modes_original_rxgain_9280_2, + ARRAY_SIZE(ar9280Modes_original_rxgain_9280_2), 6); + } +} + +static void ath9k_hw_init_txgain_ini(struct ath_hw *ah) +{ + u32 txgain_type; + + if (ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV) >= AR5416_EEP_MINOR_VER_19) { + txgain_type = ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE); + + if (txgain_type == AR5416_EEP_TXGAIN_HIGH_POWER) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9280Modes_high_power_tx_gain_9280_2, + ARRAY_SIZE(ar9280Modes_high_power_tx_gain_9280_2), 6); + else + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9280Modes_original_tx_gain_9280_2, + ARRAY_SIZE(ar9280Modes_original_tx_gain_9280_2), 6); + } else { + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9280Modes_original_tx_gain_9280_2, + ARRAY_SIZE(ar9280Modes_original_tx_gain_9280_2), 6); + } +} + +static int ath9k_hw_post_attach(struct ath_hw *ah) +{ + int ecode; + + if (!ath9k_hw_chip_test(ah)) + return -ENODEV; + + ecode = ath9k_hw_rf_claim(ah); + if (ecode != 0) + return ecode; + + ecode = ath9k_hw_eeprom_attach(ah); + if (ecode != 0) + return ecode; + + DPRINTF(ah->ah_sc, ATH_DBG_CONFIG, "Eeprom VER: %d, REV: %d\n", + ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah)); + + 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 struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc, + int *status) +{ + struct ath_hw *ah; + int ecode; + u32 i, j; + + ah = ath9k_hw_newstate(devid, sc, status); + if (ah == NULL) + return NULL; + + ath9k_hw_set_defaults(ah); + + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { + DPRINTF(sc, ATH_DBG_FATAL, "Couldn't reset chip\n"); + ecode = -EIO; + goto bad; + } + + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) { + DPRINTF(sc, ATH_DBG_FATAL, "Couldn't wakeup chip\n"); + ecode = -EIO; + goto bad; + } + + if (ah->config.serialize_regmode == SER_REG_MODE_AUTO) { + if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI || + (AR_SREV_9280(ah) && !ah->is_pciexpress)) { + ah->config.serialize_regmode = + SER_REG_MODE_ON; + } else { + ah->config.serialize_regmode = + SER_REG_MODE_OFF; + } + } + + DPRINTF(sc, ATH_DBG_RESET, "serialize_regmode is %d\n", + ah->config.serialize_regmode); + + if ((ah->hw_version.macVersion != AR_SREV_VERSION_5416_PCI) && + (ah->hw_version.macVersion != AR_SREV_VERSION_5416_PCIE) && + (ah->hw_version.macVersion != AR_SREV_VERSION_9160) && + (!AR_SREV_9100(ah)) && (!AR_SREV_9280(ah)) && (!AR_SREV_9285(ah))) { + DPRINTF(sc, ATH_DBG_FATAL, + "Mac Chip Rev 0x%02x.%x is not supported by " + "this driver\n", ah->hw_version.macVersion, + ah->hw_version.macRev); + ecode = -EOPNOTSUPP; + goto bad; + } + + if (AR_SREV_9100(ah)) { + ah->iq_caldata.calData = &iq_cal_multi_sample; + ah->supp_cals = IQ_MISMATCH_CAL; + ah->is_pciexpress = false; + } + ah->hw_version.phyRev = REG_READ(ah, AR_PHY_CHIP_ID); + + if (AR_SREV_9160_10_OR_LATER(ah)) { + if (AR_SREV_9280_10_OR_LATER(ah)) { + ah->iq_caldata.calData = &iq_cal_single_sample; + ah->adcgain_caldata.calData = + &adc_gain_cal_single_sample; + ah->adcdc_caldata.calData = + &adc_dc_cal_single_sample; + ah->adcdc_calinitdata.calData = + &adc_init_dc_cal; + } else { + ah->iq_caldata.calData = &iq_cal_multi_sample; + ah->adcgain_caldata.calData = + &adc_gain_cal_multi_sample; + ah->adcdc_caldata.calData = + &adc_dc_cal_multi_sample; + ah->adcdc_calinitdata.calData = + &adc_init_dc_cal; + } + ah->supp_cals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; + } + + ah->ani_function = ATH9K_ANI_ALL; + if (AR_SREV_9280_10_OR_LATER(ah)) + ah->ani_function &= ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL; + + if (AR_SREV_9285_12_OR_LATER(ah)) { + + INIT_INI_ARRAY(&ah->iniModes, ar9285Modes_9285_1_2, + ARRAY_SIZE(ar9285Modes_9285_1_2), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar9285Common_9285_1_2, + ARRAY_SIZE(ar9285Common_9285_1_2), 2); + + if (ah->config.pcie_clock_req) { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9285PciePhy_clkreq_off_L1_9285_1_2, + ARRAY_SIZE(ar9285PciePhy_clkreq_off_L1_9285_1_2), 2); + } else { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9285PciePhy_clkreq_always_on_L1_9285_1_2, + ARRAY_SIZE(ar9285PciePhy_clkreq_always_on_L1_9285_1_2), + 2); + } + } else if (AR_SREV_9285_10_OR_LATER(ah)) { + INIT_INI_ARRAY(&ah->iniModes, ar9285Modes_9285, + ARRAY_SIZE(ar9285Modes_9285), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar9285Common_9285, + ARRAY_SIZE(ar9285Common_9285), 2); + + if (ah->config.pcie_clock_req) { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9285PciePhy_clkreq_off_L1_9285, + ARRAY_SIZE(ar9285PciePhy_clkreq_off_L1_9285), 2); + } else { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9285PciePhy_clkreq_always_on_L1_9285, + ARRAY_SIZE(ar9285PciePhy_clkreq_always_on_L1_9285), 2); + } + } else if (AR_SREV_9280_20_OR_LATER(ah)) { + INIT_INI_ARRAY(&ah->iniModes, ar9280Modes_9280_2, + ARRAY_SIZE(ar9280Modes_9280_2), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar9280Common_9280_2, + ARRAY_SIZE(ar9280Common_9280_2), 2); + + if (ah->config.pcie_clock_req) { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9280PciePhy_clkreq_off_L1_9280, + ARRAY_SIZE(ar9280PciePhy_clkreq_off_L1_9280),2); + } else { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9280PciePhy_clkreq_always_on_L1_9280, + ARRAY_SIZE(ar9280PciePhy_clkreq_always_on_L1_9280), 2); + } + INIT_INI_ARRAY(&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(&ah->iniModes, ar9280Modes_9280, + ARRAY_SIZE(ar9280Modes_9280), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar9280Common_9280, + ARRAY_SIZE(ar9280Common_9280), 2); + } else if (AR_SREV_9160_10_OR_LATER(ah)) { + INIT_INI_ARRAY(&ah->iniModes, ar5416Modes_9160, + ARRAY_SIZE(ar5416Modes_9160), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar5416Common_9160, + ARRAY_SIZE(ar5416Common_9160), 2); + INIT_INI_ARRAY(&ah->iniBank0, ar5416Bank0_9160, + ARRAY_SIZE(ar5416Bank0_9160), 2); + INIT_INI_ARRAY(&ah->iniBB_RfGain, ar5416BB_RfGain_9160, + ARRAY_SIZE(ar5416BB_RfGain_9160), 3); + INIT_INI_ARRAY(&ah->iniBank1, ar5416Bank1_9160, + ARRAY_SIZE(ar5416Bank1_9160), 2); + INIT_INI_ARRAY(&ah->iniBank2, ar5416Bank2_9160, + ARRAY_SIZE(ar5416Bank2_9160), 2); + INIT_INI_ARRAY(&ah->iniBank3, ar5416Bank3_9160, + ARRAY_SIZE(ar5416Bank3_9160), 3); + INIT_INI_ARRAY(&ah->iniBank6, ar5416Bank6_9160, + ARRAY_SIZE(ar5416Bank6_9160), 3); + INIT_INI_ARRAY(&ah->iniBank6TPC, ar5416Bank6TPC_9160, + ARRAY_SIZE(ar5416Bank6TPC_9160), 3); + INIT_INI_ARRAY(&ah->iniBank7, ar5416Bank7_9160, + ARRAY_SIZE(ar5416Bank7_9160), 2); + if (AR_SREV_9160_11(ah)) { + INIT_INI_ARRAY(&ah->iniAddac, + ar5416Addac_91601_1, + ARRAY_SIZE(ar5416Addac_91601_1), 2); + } else { + INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac_9160, + ARRAY_SIZE(ar5416Addac_9160), 2); + } + } else if (AR_SREV_9100_OR_LATER(ah)) { + INIT_INI_ARRAY(&ah->iniModes, ar5416Modes_9100, + ARRAY_SIZE(ar5416Modes_9100), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar5416Common_9100, + ARRAY_SIZE(ar5416Common_9100), 2); + INIT_INI_ARRAY(&ah->iniBank0, ar5416Bank0_9100, + ARRAY_SIZE(ar5416Bank0_9100), 2); + INIT_INI_ARRAY(&ah->iniBB_RfGain, ar5416BB_RfGain_9100, + ARRAY_SIZE(ar5416BB_RfGain_9100), 3); + INIT_INI_ARRAY(&ah->iniBank1, ar5416Bank1_9100, + ARRAY_SIZE(ar5416Bank1_9100), 2); + INIT_INI_ARRAY(&ah->iniBank2, ar5416Bank2_9100, + ARRAY_SIZE(ar5416Bank2_9100), 2); + INIT_INI_ARRAY(&ah->iniBank3, ar5416Bank3_9100, + ARRAY_SIZE(ar5416Bank3_9100), 3); + INIT_INI_ARRAY(&ah->iniBank6, ar5416Bank6_9100, + ARRAY_SIZE(ar5416Bank6_9100), 3); + INIT_INI_ARRAY(&ah->iniBank6TPC, ar5416Bank6TPC_9100, + ARRAY_SIZE(ar5416Bank6TPC_9100), 3); + INIT_INI_ARRAY(&ah->iniBank7, ar5416Bank7_9100, + ARRAY_SIZE(ar5416Bank7_9100), 2); + INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac_9100, + ARRAY_SIZE(ar5416Addac_9100), 2); + } else { + INIT_INI_ARRAY(&ah->iniModes, ar5416Modes, + ARRAY_SIZE(ar5416Modes), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar5416Common, + ARRAY_SIZE(ar5416Common), 2); + INIT_INI_ARRAY(&ah->iniBank0, ar5416Bank0, + ARRAY_SIZE(ar5416Bank0), 2); + INIT_INI_ARRAY(&ah->iniBB_RfGain, ar5416BB_RfGain, + ARRAY_SIZE(ar5416BB_RfGain), 3); + INIT_INI_ARRAY(&ah->iniBank1, ar5416Bank1, + ARRAY_SIZE(ar5416Bank1), 2); + INIT_INI_ARRAY(&ah->iniBank2, ar5416Bank2, + ARRAY_SIZE(ar5416Bank2), 2); + INIT_INI_ARRAY(&ah->iniBank3, ar5416Bank3, + ARRAY_SIZE(ar5416Bank3), 3); + INIT_INI_ARRAY(&ah->iniBank6, ar5416Bank6, + ARRAY_SIZE(ar5416Bank6), 3); + INIT_INI_ARRAY(&ah->iniBank6TPC, ar5416Bank6TPC, + ARRAY_SIZE(ar5416Bank6TPC), 3); + INIT_INI_ARRAY(&ah->iniBank7, ar5416Bank7, + ARRAY_SIZE(ar5416Bank7), 2); + INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac, + ARRAY_SIZE(ar5416Addac), 2); + } + + if (ah->is_pciexpress) + ath9k_hw_configpcipowersave(ah, 0); + else + ath9k_hw_disablepcie(ah); + + ecode = ath9k_hw_post_attach(ah); + if (ecode != 0) + goto bad; + + if (AR_SREV_9285_12_OR_LATER(ah)) { + u32 txgain_type = ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE); + + /* txgain table */ + if (txgain_type == AR5416_EEP_TXGAIN_HIGH_POWER) { + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9285Modes_high_power_tx_gain_9285_1_2, + ARRAY_SIZE(ar9285Modes_high_power_tx_gain_9285_1_2), 6); + } else { + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9285Modes_original_tx_gain_9285_1_2, + ARRAY_SIZE(ar9285Modes_original_tx_gain_9285_1_2), 6); + } + + } + + /* rxgain table */ + if (AR_SREV_9280_20(ah)) + ath9k_hw_init_rxgain_ini(ah); + + /* txgain table */ + if (AR_SREV_9280_20(ah)) + ath9k_hw_init_txgain_ini(ah); + + ath9k_hw_fill_cap_info(ah); + + if ((ah->hw_version.devid == AR9280_DEVID_PCI) && + test_bit(ATH9K_MODE_11A, ah->caps.wireless_modes)) { + + /* EEPROM Fixup */ + for (i = 0; i < ah->iniModes.ia_rows; i++) { + u32 reg = INI_RA(&ah->iniModes, i, 0); + + for (j = 1; j < ah->iniModes.ia_columns; j++) { + u32 val = INI_RA(&ah->iniModes, i, j); + + INI_RA(&ah->iniModes, i, j) = + ath9k_hw_ini_fixup(ah, + &ah->eeprom.def, + reg, val); + } + } + } + + ecode = ath9k_hw_init_macaddr(ah); + if (ecode != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "Failed to initialize MAC address\n"); + goto bad; + } + + if (AR_SREV_9285(ah)) + ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S); + else + ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S); + + ath9k_init_nfcal_hist_buffer(ah); + + return ah; +bad: + if (ah) + ath9k_hw_detach(ah); + if (status) + *status = ecode; + + return NULL; +} + +static void ath9k_hw_init_bb(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + u32 synthDelay; + + synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; + if (IS_CHAN_B(chan)) + synthDelay = (4 * synthDelay) / 22; + else + synthDelay /= 10; + + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + + udelay(synthDelay + BASE_ACTIVATE_DELAY); +} + +static void ath9k_hw_init_qos(struct ath_hw *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_init_pll(struct ath_hw *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, 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_init_chain_masks(struct ath_hw *ah) +{ + int rx_chainmask, tx_chainmask; + + rx_chainmask = ah->rxchainmask; + tx_chainmask = ah->txchainmask; + + switch (rx_chainmask) { + case 0x5: + REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, + AR_PHY_SWAP_ALT_CHAIN); + case 0x3: + if (((ah)->hw_version.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: + 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_init_interrupt_masks(struct ath_hw *ah, + enum nl80211_iftype opmode) +{ + ah->mask_reg = AR_IMR_TXERR | + AR_IMR_TXURN | + AR_IMR_RXERR | + AR_IMR_RXORN | + AR_IMR_BCNMISC; + + if (ah->config.intr_mitigation) + ah->mask_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR; + else + ah->mask_reg |= AR_IMR_RXOK; + + ah->mask_reg |= AR_IMR_TXOK; + + if (opmode == NL80211_IFTYPE_AP) + ah->mask_reg |= AR_IMR_MIB; + + REG_WRITE(ah, AR_IMR, ah->mask_reg); + 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 bool ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us) +{ + if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad ack timeout %u\n", us); + 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)); + ah->acktimeout = us; + return true; + } +} + +static bool ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) +{ + if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad cts timeout %u\n", us); + 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)); + ah->ctstimeout = us; + return true; + } +} + +static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) +{ + if (tu > 0xFFFF) { + DPRINTF(ah->ah_sc, ATH_DBG_XMIT, + "bad global tx timeout %u\n", tu); + ah->globaltxtimeout = (u32) -1; + return false; + } else { + REG_RMW_FIELD(ah, AR_GTXTO, AR_GTXTO_TIMEOUT_LIMIT, tu); + ah->globaltxtimeout = tu; + return true; + } +} + +static void ath9k_hw_init_user_settings(struct ath_hw *ah) +{ + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "ah->misc_mode 0x%x\n", + ah->misc_mode); + + if (ah->misc_mode != 0) + REG_WRITE(ah, AR_PCU_MISC, + REG_READ(ah, AR_PCU_MISC) | ah->misc_mode); + if (ah->slottime != (u32) -1) + ath9k_hw_setslottime(ah, ah->slottime); + if (ah->acktimeout != (u32) -1) + ath9k_hw_set_ack_timeout(ah, ah->acktimeout); + if (ah->ctstimeout != (u32) -1) + ath9k_hw_set_cts_timeout(ah, ah->ctstimeout); + if (ah->globaltxtimeout != (u32) -1) + ath9k_hw_set_global_txtimeout(ah, ah->globaltxtimeout); +} + +const char *ath9k_hw_probe(u16 vendorid, u16 devid) +{ + return vendorid == ATHEROS_VENDOR_ID ? + ath9k_hw_devname(devid) : NULL; +} + +void ath9k_hw_detach(struct ath_hw *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); +} + +struct ath_hw *ath9k_hw_attach(u16 devid, struct ath_softc *sc, int *error) +{ + struct ath_hw *ah = NULL; + + switch (devid) { + case AR5416_DEVID_PCI: + case AR5416_DEVID_PCIE: + case AR5416_AR9100_DEVID: + case AR9160_DEVID_PCI: + case AR9280_DEVID_PCI: + case AR9280_DEVID_PCIE: + case AR9285_DEVID_PCIE: + ah = ath9k_hw_do_attach(devid, sc, error); + break; + default: + *error = -ENXIO; + break; + } + + return ah; +} + +/*******/ +/* INI */ +/*******/ + +static void ath9k_hw_override_ini(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + /* + * Set the RX_ABORT and RX_DIS and clear if off only after + * RXE is set for MAC. This prevents frames with corrupted + * descriptor status. + */ + REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + + + if (!AR_SREV_5416_20_OR_LATER(ah) || + AR_SREV_9280_10_OR_LATER(ah)) + return; + + REG_WRITE(ah, 0x9800 + (651 << 2), 0x11); +} + +static u32 ath9k_hw_def_ini_fixup(struct ath_hw *ah, + struct ar5416_eeprom_def *pEepData, + u32 reg, u32 value) +{ + struct base_eep_header *pBase = &(pEepData->baseEepHeader); + + switch (ah->hw_version.devid) { + case AR9280_DEVID_PCI: + if (reg == 0x7894) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "ini VAL: %x EEPROM: %x\n", value, + (pBase->version & 0xff)); + + if ((pBase->version & 0xff) > 0x0a) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "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_EEPROM, + "PWDCLKIND Earlier Rev\n"); + } + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "final ini VAL: %x\n", value); + } + break; + } + + return value; +} + +static u32 ath9k_hw_ini_fixup(struct ath_hw *ah, + struct ar5416_eeprom_def *pEepData, + u32 reg, u32 value) +{ + if (ah->eep_map == EEP_MAP_4KBITS) + return value; + else + return ath9k_hw_def_ini_fixup(ah, pEepData, reg, value); +} + +static void ath9k_olc_init(struct ath_hw *ah) +{ + u32 i; + + for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++) + ah->originalGain[i] = + MS(REG_READ(ah, AR_PHY_TX_GAIN_TBL1 + i * 4), + AR_PHY_TX_GAIN); + ah->PDADCdelta = 0; +} + +static u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, + struct ath9k_channel *chan) +{ + u32 ctl = ath_regd_get_band_ctl(reg, chan->chan->band); + + if (IS_CHAN_B(chan)) + ctl |= CTL_11B; + else if (IS_CHAN_G(chan)) + ctl |= CTL_11G; + else + ctl |= CTL_11A; + + return ctl; +} + +static int ath9k_hw_process_ini(struct ath_hw *ah, + struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode) +{ + int i, regWrites = 0; + struct ieee80211_channel *channel = chan->chan; + u32 modesIndex, freqIndex; + + 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); + ah->eep_ops->set_addac(ah, chan); + + if (AR_SREV_5416_22_OR_LATER(ah)) { + REG_WRITE_ARRAY(&ah->iniAddac, 1, regWrites); + } else { + struct ar5416IniArray temp; + u32 addacSize = + sizeof(u32) * ah->iniAddac.ia_rows * + ah->iniAddac.ia_columns; + + memcpy(ah->addac5416_21, + ah->iniAddac.ia_array, addacSize); + + (ah->addac5416_21)[31 * ah->iniAddac.ia_columns + 1] = 0; + + temp.ia_array = ah->addac5416_21; + temp.ia_columns = ah->iniAddac.ia_columns; + temp.ia_rows = 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 < ah->iniModes.ia_rows; i++) { + u32 reg = INI_RA(&ah->iniModes, i, 0); + u32 val = INI_RA(&ah->iniModes, i, modesIndex); + + REG_WRITE(ah, reg, val); + + if (reg >= 0x7800 && reg < 0x78a0 + && ah->config.analog_shiftreg) { + udelay(100); + } + + DO_DELAY(regWrites); + } + + if (AR_SREV_9280(ah)) + REG_WRITE_ARRAY(&ah->iniModesRxGain, modesIndex, regWrites); + + if (AR_SREV_9280(ah) || AR_SREV_9285_12_OR_LATER(ah)) + REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); + + for (i = 0; i < ah->iniCommon.ia_rows; i++) { + u32 reg = INI_RA(&ah->iniCommon, i, 0); + u32 val = INI_RA(&ah->iniCommon, i, 1); + + REG_WRITE(ah, reg, val); + + if (reg >= 0x7800 && reg < 0x78a0 + && ah->config.analog_shiftreg) { + 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(&ah->iniModesAdditional, modesIndex, + regWrites); + } + + ath9k_hw_override_ini(ah, chan); + ath9k_hw_set_regs(ah, chan, macmode); + ath9k_hw_init_chain_masks(ah); + + if (OLC_FOR_AR9280_20_LATER) + ath9k_olc_init(ah); + + ah->eep_ops->set_txpower(ah, chan, + ath9k_regd_get_ctl(&ah->regulatory, chan), + channel->max_antenna_gain * 2, + channel->max_power * 2, + min((u32) MAX_RATE_POWER, + (u32) ah->regulatory.power_limit)); + + if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "ar5416SetRfRegs failed\n"); + return -EIO; + } + + return 0; +} + +/****************************************/ +/* Reset and Channel Switching Routines */ +/****************************************/ + +static void ath9k_hw_set_rfmode(struct ath_hw *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 void ath9k_hw_mark_phy_inactive(struct ath_hw *ah) +{ + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); +} + +static inline void ath9k_hw_set_dma(struct ath_hw *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->tx_trig_level); + + 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); + } +} + +static void ath9k_hw_set_operating_mode(struct ath_hw *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 NL80211_IFTYPE_AP: + 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 NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + 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 NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_MONITOR: + REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE); + break; + } +} + +static inline void ath9k_hw_get_delta_slope_vals(struct ath_hw *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_hw *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 bool ath9k_hw_set_reset(struct ath_hw *ah, int type) +{ + u32 rst_flags; + u32 tmpReg; + + if (AR_SREV_9100(ah)) { + u32 val = REG_READ(ah, AR_RTC_DERIVED_CLK); + val &= ~AR_RTC_DERIVED_CLK_PERIOD; + val |= SM(1, AR_RTC_DERIVED_CLK_PERIOD); + REG_WRITE(ah, AR_RTC_DERIVED_CLK, val); + (void)REG_READ(ah, AR_RTC_DERIVED_CLK); + } + + 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, AR_RTC_RC, rst_flags); + udelay(50); + + REG_WRITE(ah, AR_RTC_RC, 0); + if (!ath9k_hw_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0, AH_WAIT_TIMEOUT)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "RTC stuck in MAC reset\n"); + 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 bool ath9k_hw_set_reset_power_on(struct ath_hw *ah) +{ + REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | + AR_RTC_FORCE_WAKE_ON_INT); + + REG_WRITE(ah, AR_RTC_RESET, 0); + udelay(2); + REG_WRITE(ah, AR_RTC_RESET, 1); + + if (!ath9k_hw_wait(ah, + AR_RTC_STATUS, + AR_RTC_STATUS_M, + AR_RTC_STATUS_ON, + AH_WAIT_TIMEOUT)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "RTC not waking up\n"); + 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_hw *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); + case ATH9K_RESET_WARM: + case ATH9K_RESET_COLD: + return ath9k_hw_set_reset(ah, type); + default: + return false; + } +} + +static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode) +{ + u32 phymode; + u32 enableDacFifo = 0; + + if (AR_SREV_9285_10_OR_LATER(ah)) + enableDacFifo = (REG_READ(ah, AR_PHY_TURBO) & + AR_PHY_FC_ENABLE_DAC_FIFO); + + phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40 + | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH | enableDacFifo; + + 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 (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 bool ath9k_hw_chip_reset(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + if (OLC_FOR_AR9280_20_LATER) { + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) + return false; + } else if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM)) + return false; + + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + return false; + + ah->chip_fullsleep = false; + ath9k_hw_init_pll(ah, chan); + ath9k_hw_set_rfmode(ah, chan); + + return true; +} + +static bool ath9k_hw_channel_change(struct ath_hw *ah, + struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode) +{ + struct ieee80211_channel *channel = chan->chan; + u32 synthDelay, qnum; + + for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { + if (ath9k_hw_numtxpending(ah, qnum)) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "Transmit frames pending on queue %d\n", 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, AH_WAIT_TIMEOUT)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Could not kill baseband RX\n"); + return false; + } + + ath9k_hw_set_regs(ah, chan, macmode); + + if (AR_SREV_9280_10_OR_LATER(ah)) { + ath9k_hw_ar9280_set_channel(ah, chan); + } else { + if (!(ath9k_hw_set_channel(ah, chan))) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Failed to set channel\n"); + return false; + } + } + + ah->eep_ops->set_txpower(ah, chan, + ath9k_regd_get_ctl(&ah->regulatory, chan), + channel->max_antenna_gain * 2, + channel->max_power * 2, + min((u32) MAX_RATE_POWER, + (u32) ah->regulatory.power_limit)); + + synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; + if (IS_CHAN_B(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 void ath9k_hw_9280_spur_mitigate(struct ath_hw *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->config.spurmode = SPUR_ENABLE_EEPROM; + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + cur_bb_spur = ah->eep_ops->get_spur_channel(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)) { + + /* workaround for gcc bug #37014 */ + volatile int tmp_v = abs(cur_vit_mask - bin); + + if (tmp_v < 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_hw *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 = ah->eep_ops->get_spur_channel(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)) { + + /* workaround for gcc bug #37014 */ + volatile int tmp_v = abs(cur_vit_mask - bin); + + if (tmp_v < 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); +} + +int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, + bool bChannelChange) +{ + u32 saveLedState; + struct ath_softc *sc = ah->ah_sc; + struct ath9k_channel *curchan = ah->curchan; + u32 saveDefAntenna; + u32 macStaId1; + int i, rx_chainmask, r; + + ah->extprotspacing = sc->ht_extprotspacing; + ah->txchainmask = sc->tx_chainmask; + ah->rxchainmask = sc->rx_chainmask; + + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + return -EIO; + + if (curchan) + ath9k_hw_getnf(ah, curchan); + + if (bChannelChange && + (ah->chip_fullsleep != true) && + (ah->curchan != NULL) && + (chan->channel != ah->curchan->channel) && + ((chan->channelFlags & CHANNEL_ALL) == + (ah->curchan->channelFlags & CHANNEL_ALL)) && + (!AR_SREV_9280(ah) || (!IS_CHAN_A_5MHZ_SPACED(chan) && + !IS_CHAN_A_5MHZ_SPACED(ah->curchan)))) { + + if (ath9k_hw_channel_change(ah, chan, sc->tx_chan_width)) { + ath9k_hw_loadnf(ah, ah->curchan); + ath9k_hw_start_nfcal(ah); + return 0; + } + } + + 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_FATAL, "Chip reset failed\n"); + return -EINVAL; + } + + if (AR_SREV_9280_10_OR_LATER(ah)) + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); + + r = ath9k_hw_process_ini(ah, chan, sc->tx_chan_width); + if (r) + return r; + + /* Setup MFP options for CCMP */ + if (AR_SREV_9280_20_OR_LATER(ah)) { + /* Mask Retry(b11), PwrMgt(b12), MoreData(b13) to 0 in mgmt + * frames when constructing CCMP AAD. */ + REG_RMW_FIELD(ah, AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT, + 0xc7ff); + ah->sw_mgmt_crypto = false; + } else if (AR_SREV_9160_10_OR_LATER(ah)) { + /* Disable hardware crypto for management frames */ + REG_CLR_BIT(ah, AR_PCU_MISC_MODE2, + AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE); + REG_SET_BIT(ah, AR_PCU_MISC_MODE2, + AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT); + ah->sw_mgmt_crypto = true; + } else + ah->sw_mgmt_crypto = true; + + 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); + + ah->eep_ops->set_board_values(ah, chan); + + ath9k_hw_decrease_chain_power(ah, chan); + + REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(ah->macaddr)); + REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(ah->macaddr + 4) + | macStaId1 + | AR_STA_ID1_RTS_USE_DEF + | (ah->config. + ack_6mb ? AR_STA_ID1_ACKCTS_6MB : 0) + | ah->sta_id1_defaults); + ath9k_hw_set_operating_mode(ah, ah->opmode); + + REG_WRITE(ah, AR_BSSMSKL, get_unaligned_le32(sc->bssidmask)); + REG_WRITE(ah, AR_BSSMSKU, get_unaligned_le16(sc->bssidmask + 4)); + + REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); + + REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(sc->curbssid)); + REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(sc->curbssid + 4) | + ((sc->curaid & 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)) + ath9k_hw_ar9280_set_channel(ah, chan); + else + if (!(ath9k_hw_set_channel(ah, chan))) + return -EIO; + + for (i = 0; i < AR_NUM_DCU; i++) + REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); + + ah->intr_txqs = 0; + for (i = 0; i < ah->caps.total_queues; i++) + ath9k_hw_resettxqueue(ah, i); + + ath9k_hw_init_interrupt_masks(ah, ah->opmode); + ath9k_hw_init_qos(ah); + +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) + if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) + ath9k_enable_rfkill(ah); +#endif + ath9k_hw_init_user_settings(ah); + + 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 (ah->config.intr_mitigation) { + 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)) + return -EIO;; + + rx_chainmask = 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, + "CFG Byte Swap Set 0x%x\n", 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, + "Setting CFG 0x%x\n", REG_READ(ah, AR_CFG)); + } + } else { +#ifdef __BIG_ENDIAN + REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD); +#endif + } + + return 0; +} + +/************************/ +/* Key Cache Management */ +/************************/ + +bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry) +{ + u32 keyType; + + if (entry >= ah->caps.keycache_size) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "keychache entry %u out of range\n", 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->curchan == NULL) + return true; + + return true; +} + +bool ath9k_hw_keysetmac(struct ath_hw *ah, u16 entry, const u8 *mac) +{ + u32 macHi, macLo; + + if (entry >= ah->caps.keycache_size) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "keychache entry %u out of range\n", 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_hw *ah, u16 entry, + const struct ath9k_keyval *k, + const u8 *mac) +{ + const struct ath9k_hw_capabilities *pCap = &ah->caps; + u32 key0, key1, key2, key3, key4; + u32 keyType; + + if (entry >= pCap->keycache_size) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "keycache entry %u out of range\n", 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->hw_caps & ATH9K_HW_CAP_CIPHER_AESCCM)) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "AES-CCM not supported by mac rev 0x%x\n", + ah->hw_version.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->keycache_size) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "entry %u inappropriate for TKIP\n", entry); + return false; + } + break; + case ATH9K_CIPHER_WEP: + if (k->kv_len < WLAN_KEY_LEN_WEP40) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "WEP key length %u too small\n", k->kv_len); + return false; + } + if (k->kv_len <= WLAN_KEY_LEN_WEP40) + keyType = AR_KEYTABLE_TYPE_40; + else if (k->kv_len <= WLAN_KEY_LEN_WEP104) + 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_FATAL, + "cipher %u not supported\n", k->kv_type); + return false; + } + + key0 = get_unaligned_le32(k->kv_val + 0); + key1 = get_unaligned_le16(k->kv_val + 4); + key2 = get_unaligned_le32(k->kv_val + 6); + key3 = get_unaligned_le16(k->kv_val + 10); + key4 = get_unaligned_le32(k->kv_val + 12); + if (k->kv_len <= WLAN_KEY_LEN_WEP104) + key4 &= 0xff; + + /* + * Note: Key cache registers access special memory area that requires + * two 32-bit writes to actually update the values in the internal + * memory. Consequently, the exact order and pairs used here must be + * maintained. + */ + + if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) { + u16 micentry = entry + 64; + + /* + * Write inverted key[47:0] first to avoid Michael MIC errors + * on frames that could be sent or received at the same time. + * The correct key will be written in the end once everything + * else is ready. + */ + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1); + + /* Write key[95:48] */ + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); + + /* Write key[127:96] and key type */ + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); + + /* Write MAC address for the entry */ + (void) ath9k_hw_keysetmac(ah, entry, mac); + + if (ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA) { + /* + * TKIP uses two key cache entries: + * Michael MIC TX/RX keys in the same key cache entry + * (idx = main index + 64): + * key0 [31:0] = RX key [31:0] + * key1 [15:0] = TX key [31:16] + * key1 [31:16] = reserved + * key2 [31:0] = RX key [63:32] + * key3 [15:0] = TX key [15:0] + * key3 [31:16] = reserved + * key4 [31:0] = TX key [63:32] + */ + 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); + + /* Write RX[31:0] and TX[31:16] */ + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1); + + /* Write RX[63:32] and TX[15:0] */ + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3); + + /* Write TX[63:32] and keyType(reserved) */ + REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), + AR_KEYTABLE_TYPE_CLR); + + } else { + /* + * TKIP uses four key cache entries (two for group + * keys): + * Michael MIC TX/RX keys are in different key cache + * entries (idx = main index + 64 for TX and + * main index + 32 + 96 for RX): + * key0 [31:0] = TX/RX MIC key [31:0] + * key1 [31:0] = reserved + * key2 [31:0] = TX/RX MIC key [63:32] + * key3 [31:0] = reserved + * key4 [31:0] = reserved + * + * Upper layer code will call this function separately + * for TX and RX keys when these registers offsets are + * used. + */ + u32 mic0, mic2; + + mic0 = get_unaligned_le32(k->kv_mic + 0); + mic2 = get_unaligned_le32(k->kv_mic + 4); + + /* Write MIC key[31:0] */ + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); + + /* Write MIC key[63:32] */ + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); + + /* Write TX[63:32] and keyType(reserved) */ + REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), + AR_KEYTABLE_TYPE_CLR); + } + + /* MAC address registers are reserved for the MIC entry */ + REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0); + + /* + * Write the correct (un-inverted) key[47:0] last to enable + * TKIP now that all other registers are set with correct + * values. + */ + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); + } else { + /* Write key[47:0] */ + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); + + /* Write key[95:48] */ + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); + + /* Write key[127:96] and key type */ + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); + + /* Write MAC address for the entry */ + (void) ath9k_hw_keysetmac(ah, entry, mac); + } + + return true; +} + +bool ath9k_hw_keyisvalid(struct ath_hw *ah, u16 entry) +{ + if (entry < ah->caps.keycache_size) { + u32 val = REG_READ(ah, AR_KEYTABLE_MAC1(entry)); + if (val & AR_KEYTABLE_VALID) + return true; + } + return false; +} + +/******************************/ +/* Power Management (Chipset) */ +/******************************/ + +static void ath9k_set_power_sleep(struct ath_hw *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, (AR_RTC_RESET), + AR_RTC_RESET_EN); + } +} + +static void ath9k_set_power_network_sleep(struct ath_hw *ah, int setChip) +{ + REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); + if (setChip) { + struct ath9k_hw_capabilities *pCap = &ah->caps; + + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { + 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_hw *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_FATAL, + "Failed to wakeup in %uus\n", 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_hw *ah, enum ath9k_power_mode mode) +{ + int status = true, setChip = true; + static const char *modes[] = { + "AWAKE", + "FULL-SLEEP", + "NETWORK SLEEP", + "UNDEFINED" + }; + + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s -> %s\n", + modes[ah->power_mode], modes[mode]); + + 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); + ah->chip_fullsleep = true; + break; + case ATH9K_PM_NETWORK_SLEEP: + ath9k_set_power_network_sleep(ah, setChip); + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Unknown power mode %u\n", mode); + return false; + } + ah->power_mode = mode; + + return status; +} + +/* + * Helper for ASPM support. + * + * Disable PLL when in L0s as well as receiver clock when in L1. + * This power saving option must be enabled through the SerDes. + * + * Programming the SerDes must go through the same 288 bit serial shift + * register as the other analog registers. Hence the 9 writes. + */ +void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore) +{ + u8 i; + + if (ah->is_pciexpress != true) + return; + + /* Do not touch SerDes registers */ + if (ah->config.pcie_powersave_enable == 2) + return; + + /* Nothing to do on restore for 11N */ + if (restore) + return; + + if (AR_SREV_9280_20_OR_LATER(ah)) { + /* + * AR9280 2.0 or later chips use SerDes values from the + * initvals.h initialized depending on chipset during + * ath9k_hw_do_attach() + */ + for (i = 0; i < ah->iniPcieSerdes.ia_rows; i++) { + REG_WRITE(ah, INI_RA(&ah->iniPcieSerdes, i, 0), + INI_RA(&ah->iniPcieSerdes, i, 1)); + } + } else if (AR_SREV_9280(ah) && + (ah->hw_version.macRev == AR_SREV_REVISION_9280_10)) { + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + + /* RX shut off when elecidle is asserted */ + REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019); + REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820); + REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560); + + /* Shut off CLKREQ active in L1 */ + if (ah->config.pcie_clock_req) + 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); + + /* Load the new settings */ + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + + } else { + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + + /* RX shut off when elecidle is asserted */ + REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); + REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); + REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); + + /* + * Ignore ah->ah_config.pcie_clock_req setting for + * pre-AR9280 11n + */ + 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); + + /* Load the new settings */ + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + } + + udelay(1000); + + /* set bit 19 to allow forcing of pcie core into L1 state */ + REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + + /* Several PCIe massages to ensure proper behaviour */ + if (ah->config.pcie_waen) { + REG_WRITE(ah, AR_WA, ah->config.pcie_waen); + } else { + if (AR_SREV_9285(ah)) + REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); + /* + * On AR9280 chips bit 22 of 0x4004 needs to be set to + * otherwise card may disappear. + */ + else if (AR_SREV_9280(ah)) + REG_WRITE(ah, AR_WA, AR9280_WA_DEFAULT); + else + REG_WRITE(ah, AR_WA, AR_WA_DEFAULT); + } +} + +/**********************/ +/* Interrupt Handling */ +/**********************/ + +bool ath9k_hw_intrpend(struct ath_hw *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_hw *ah, enum ath9k_int *masked) +{ + u32 isr = 0; + u32 mask2 = 0; + struct ath9k_hw_capabilities *pCap = &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) { + 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; + if (isr2 & AR_ISR_S2_TSFOOR) + mask2 |= ATH9K_INT_TSFOOR; + } + + isr = REG_READ(ah, AR_ISR_RAC); + if (isr == 0xffffffff) { + *masked = 0; + return false; + } + + *masked = isr & ATH9K_INT_COMMON; + + if (ah->config.intr_mitigation) { + 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); + ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXOK); + ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXDESC); + + s1_s = REG_READ(ah, AR_ISR_S1_S); + ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXERR); + ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXEOL); + } + + if (isr & AR_ISR_RXORN) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "receive FIFO overrun interrupt\n"); + } + + if (!AR_SREV_9100(ah)) { + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { + 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, + "received PCI FATAL interrupt\n"); + } + if (sync_cause & AR_INTR_SYNC_HOST1_PERR) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "received PCI PERR interrupt\n"); + } + *masked |= ATH9K_INT_FATAL; + } + if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "AR_INTR_SYNC_RADM_CPL_TIMEOUT\n"); + 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, + "AR_INTR_SYNC_LOCAL_TIMEOUT\n"); + } + + 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_hw *ah) +{ + return ah->mask_reg; +} + +enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints) +{ + u32 omask = ah->mask_reg; + u32 mask, mask2; + struct ath9k_hw_capabilities *pCap = &ah->caps; + + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints); + + if (omask & ATH9K_INT_GLOBAL) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "disable IER\n"); + 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 (ah->txok_interrupt_mask) + mask |= AR_IMR_TXOK; + if (ah->txdesc_interrupt_mask) + mask |= AR_IMR_TXDESC; + if (ah->txerr_interrupt_mask) + mask |= AR_IMR_TXERR; + if (ah->txeol_interrupt_mask) + mask |= AR_IMR_TXEOL; + } + if (ints & ATH9K_INT_RX) { + mask |= AR_IMR_RXERR; + if (ah->config.intr_mitigation) + mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM; + else + mask |= AR_IMR_RXOK | AR_IMR_RXDESC; + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) + 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_TSFOOR) + mask2 |= AR_IMR_S2_TSFOOR; + } + + 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, "new IMR 0x%x\n", 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); + ah->mask_reg = ints; + + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { + 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, "enable IER\n"); + 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; +} + +/*******************/ +/* Beacon Handling */ +/*******************/ + +void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) +{ + int flags = 0; + + ah->beacon_interval = beacon_period; + + switch (ah->opmode) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_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 NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + 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 + + (ah->atim_window ? ah-> + atim_window : 1))); + flags |= AR_NDP_TIMER_EN; + case NL80211_IFTYPE_AP: + 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->config. + dma_beacon_response_time)); + REG_WRITE(ah, AR_NEXT_SWBA, + TU_TO_USEC(next_beacon - + ah->config. + sw_beacon_response_time)); + flags |= + AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN; + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, + "%s: unsupported opmode: %d\n", + __func__, ah->opmode); + return; + 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_hw *ah, + const struct ath9k_beacon_state *bs) +{ + u32 nextTbtt, beaconintval, dtimperiod, beacontimeout; + struct ath9k_hw_capabilities *pCap = &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, "next DTIM %d\n", bs->bs_nextdtim); + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "next beacon %d\n", nextTbtt); + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "beacon period %d\n", beaconintval); + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "DTIM period %d\n", 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->hw_caps & ATH9K_HW_CAP_AUTOSLEEP) + 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); + + /* TSF Out of Range Threshold */ + REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold); +} + +/*******************/ +/* HW Capabilities */ +/*******************/ + +void ath9k_hw_fill_cap_info(struct ath_hw *ah) +{ + struct ath9k_hw_capabilities *pCap = &ah->caps; + u16 capField = 0, eeval; + + eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_0); + ah->regulatory.current_rd = eeval; + + eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_1); + if (AR_SREV_9285_10_OR_LATER(ah)) + eeval |= AR9285_RDEXT_DEFAULT; + ah->regulatory.current_rd_ext = eeval; + + capField = ah->eep_ops->get_eeprom(ah, EEP_OP_CAP); + + if (ah->opmode != NL80211_IFTYPE_AP && + ah->hw_version.subvendorid == AR_SUBVENDOR_ID_NEW_A) { + if (ah->regulatory.current_rd == 0x64 || + ah->regulatory.current_rd == 0x65) + ah->regulatory.current_rd += 5; + else if (ah->regulatory.current_rd == 0x41) + ah->regulatory.current_rd = 0x43; + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "regdomain mapped to 0x%x\n", ah->regulatory.current_rd); + } + + eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE); + bitmap_zero(pCap->wireless_modes, ATH9K_MODE_MAX); + + if (eeval & AR5416_OPFLAGS_11A) { + set_bit(ATH9K_MODE_11A, pCap->wireless_modes); + if (ah->config.ht_enable) { + if (!(eeval & AR5416_OPFLAGS_N_5G_HT20)) + set_bit(ATH9K_MODE_11NA_HT20, + pCap->wireless_modes); + if (!(eeval & AR5416_OPFLAGS_N_5G_HT40)) { + set_bit(ATH9K_MODE_11NA_HT40PLUS, + pCap->wireless_modes); + set_bit(ATH9K_MODE_11NA_HT40MINUS, + pCap->wireless_modes); + } + } + } + + if (eeval & AR5416_OPFLAGS_11G) { + set_bit(ATH9K_MODE_11B, pCap->wireless_modes); + set_bit(ATH9K_MODE_11G, pCap->wireless_modes); + if (ah->config.ht_enable) { + if (!(eeval & AR5416_OPFLAGS_N_2G_HT20)) + set_bit(ATH9K_MODE_11NG_HT20, + pCap->wireless_modes); + if (!(eeval & AR5416_OPFLAGS_N_2G_HT40)) { + set_bit(ATH9K_MODE_11NG_HT40PLUS, + pCap->wireless_modes); + set_bit(ATH9K_MODE_11NG_HT40MINUS, + pCap->wireless_modes); + } + } + } + + pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK); + if ((ah->hw_version.devid == AR5416_DEVID_PCI) && + !(eeval & AR5416_OPFLAGS_11A)) + pCap->rx_chainmask = ath9k_hw_gpio_get(ah, 0) ? 0x5 : 0x7; + else + pCap->rx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_RX_MASK); + + if (!(AR_SREV_9280(ah) && (ah->hw_version.macRev == 0))) + ah->misc_mode |= AR_PCU_MIC_NEW_LOC_ENA; + + pCap->low_2ghz_chan = 2312; + pCap->high_2ghz_chan = 2732; + + pCap->low_5ghz_chan = 4920; + pCap->high_5ghz_chan = 6100; + + pCap->hw_caps &= ~ATH9K_HW_CAP_CIPHER_CKIP; + pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_TKIP; + pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_AESCCM; + + pCap->hw_caps &= ~ATH9K_HW_CAP_MIC_CKIP; + pCap->hw_caps |= ATH9K_HW_CAP_MIC_TKIP; + pCap->hw_caps |= ATH9K_HW_CAP_MIC_AESCCM; + + if (ah->config.ht_enable) + pCap->hw_caps |= ATH9K_HW_CAP_HT; + else + pCap->hw_caps &= ~ATH9K_HW_CAP_HT; + + pCap->hw_caps |= ATH9K_HW_CAP_GTT; + pCap->hw_caps |= ATH9K_HW_CAP_VEOL; + pCap->hw_caps |= ATH9K_HW_CAP_BSSIDMASK; + pCap->hw_caps &= ~ATH9K_HW_CAP_MCAST_KEYSEARCH; + + if (capField & AR_EEPROM_EEPCAP_MAXQCU) + pCap->total_queues = + MS(capField, AR_EEPROM_EEPCAP_MAXQCU); + else + pCap->total_queues = ATH9K_NUM_TX_QUEUES; + + if (capField & AR_EEPROM_EEPCAP_KC_ENTRIES) + pCap->keycache_size = + 1 << MS(capField, AR_EEPROM_EEPCAP_KC_ENTRIES); + else + pCap->keycache_size = AR_KEYTABLE_SIZE; + + pCap->hw_caps |= ATH9K_HW_CAP_FASTCC; + pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD; + + if (AR_SREV_9285_10_OR_LATER(ah)) + pCap->num_gpio_pins = AR9285_NUM_GPIO; + else if (AR_SREV_9280_10_OR_LATER(ah)) + pCap->num_gpio_pins = AR928X_NUM_GPIO; + else + pCap->num_gpio_pins = AR_NUM_GPIO; + + if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah)) { + pCap->hw_caps |= ATH9K_HW_CAP_CST; + pCap->rts_aggr_limit = ATH_AMPDU_LIMIT_MAX; + } else { + pCap->rts_aggr_limit = (8 * 1024); + } + + pCap->hw_caps |= ATH9K_HW_CAP_ENHANCEDPM; + +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) + ah->rfsilent = ah->eep_ops->get_eeprom(ah, EEP_RF_SILENT); + if (ah->rfsilent & EEP_RFSILENT_ENABLED) { + ah->rfkill_gpio = + MS(ah->rfsilent, EEP_RFSILENT_GPIO_SEL); + ah->rfkill_polarity = + MS(ah->rfsilent, EEP_RFSILENT_POLARITY); + + pCap->hw_caps |= ATH9K_HW_CAP_RFSILENT; + } +#endif + + if ((ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || + (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE) || + (ah->hw_version.macVersion == AR_SREV_VERSION_9160) || + (ah->hw_version.macVersion == AR_SREV_VERSION_9100) || + (ah->hw_version.macVersion == AR_SREV_VERSION_9280) || + (ah->hw_version.macVersion == AR_SREV_VERSION_9285)) + pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP; + else + pCap->hw_caps |= ATH9K_HW_CAP_AUTOSLEEP; + + if (AR_SREV_9280(ah) || AR_SREV_9285(ah)) + pCap->hw_caps &= ~ATH9K_HW_CAP_4KB_SPLITTRANS; + else + pCap->hw_caps |= ATH9K_HW_CAP_4KB_SPLITTRANS; + + if (ah->regulatory.current_rd_ext & (1 << REG_EXT_JAPAN_MIDBAND)) { + pCap->reg_cap = + 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->reg_cap = + AR_EEPROM_EEREGCAP_EN_KK_NEW_11A | + AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN; + } + + pCap->reg_cap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND; + + pCap->num_antcfg_5ghz = + ah->eep_ops->get_num_ant_config(ah, ATH9K_HAL_FREQ_BAND_5GHZ); + pCap->num_antcfg_2ghz = + ah->eep_ops->get_num_ant_config(ah, ATH9K_HAL_FREQ_BAND_2GHZ); + + if (AR_SREV_9280_10_OR_LATER(ah) && btcoex_enable) { + pCap->hw_caps |= ATH9K_HW_CAP_BT_COEX; + ah->btactive_gpio = 6; + ah->wlanactive_gpio = 5; + } +} + +bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type, + u32 capability, u32 *result) +{ + switch (type) { + case ATH9K_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 ATH9K_CAP_TKIP_MIC: + switch (capability) { + case 0: + return true; + case 1: + return (ah->sta_id1_defaults & + AR_STA_ID1_CRPT_MIC_ENABLE) ? true : + false; + } + case ATH9K_CAP_TKIP_SPLIT: + return (ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA) ? + false : true; + case ATH9K_CAP_DIVERSITY: + return (REG_READ(ah, AR_PHY_CCK_DETECT) & + AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV) ? + true : false; + case ATH9K_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 (ah->sta_id1_defaults & + AR_STA_ID1_MCAST_KSRCH) ? true : + false; + } + } + return false; + case ATH9K_CAP_TXPOW: + switch (capability) { + case 0: + return 0; + case 1: + *result = ah->regulatory.power_limit; + return 0; + case 2: + *result = ah->regulatory.max_power_level; + return 0; + case 3: + *result = ah->regulatory.tp_scale; + return 0; + } + return false; + case ATH9K_CAP_DS: + return (AR_SREV_9280_20_OR_LATER(ah) && + (ah->eep_ops->get_eeprom(ah, EEP_RC_CHAIN_MASK) == 1)) + ? false : true; + default: + return false; + } +} + +bool ath9k_hw_setcapability(struct ath_hw *ah, enum ath9k_capability_type type, + u32 capability, u32 setting, int *status) +{ + u32 v; + + switch (type) { + case ATH9K_CAP_TKIP_MIC: + if (setting) + ah->sta_id1_defaults |= + AR_STA_ID1_CRPT_MIC_ENABLE; + else + ah->sta_id1_defaults &= + ~AR_STA_ID1_CRPT_MIC_ENABLE; + return true; + case ATH9K_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 ATH9K_CAP_MCAST_KEYSRCH: + if (setting) + ah->sta_id1_defaults |= AR_STA_ID1_MCAST_KSRCH; + else + ah->sta_id1_defaults &= ~AR_STA_ID1_MCAST_KSRCH; + return true; + default: + return false; + } +} + +/****************************/ +/* GPIO / RFKILL / Antennae */ +/****************************/ + +static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *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); + } +} + +void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio) +{ + u32 gpio_shift; + + ASSERT(gpio < ah->caps.num_gpio_pins); + + gpio_shift = gpio << 1; + + REG_RMW(ah, + AR_GPIO_OE_OUT, + (AR_GPIO_OE_OUT_DRV_NO << gpio_shift), + (AR_GPIO_OE_OUT_DRV << gpio_shift)); +} + +u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) +{ +#define MS_REG_READ(x, y) \ + (MS(REG_READ(ah, AR_GPIO_IN_OUT), x##_GPIO_IN_VAL) & (AR_GPIO_BIT(y))) + + if (gpio >= ah->caps.num_gpio_pins) + return 0xffffffff; + + if (AR_SREV_9285_10_OR_LATER(ah)) + return MS_REG_READ(AR9285, gpio) != 0; + else if (AR_SREV_9280_10_OR_LATER(ah)) + return MS_REG_READ(AR928X, gpio) != 0; + else + return MS_REG_READ(AR, gpio) != 0; +} + +void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, + u32 ah_signal_type) +{ + u32 gpio_shift; + + 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)); +} + +void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val) +{ + REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio), + AR_GPIO_BIT(gpio)); +} + +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) +void ath9k_enable_rfkill(struct ath_hw *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, ah->rfkill_gpio); + REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); +} +#endif + +u32 ath9k_hw_getdefantenna(struct ath_hw *ah) +{ + return REG_READ(ah, AR_DEF_ANTENNA) & 0x7; +} + +void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna) +{ + REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7)); +} + +bool ath9k_hw_setantennaswitch(struct ath_hw *ah, + enum ath9k_ant_setting settings, + struct ath9k_channel *chan, + u8 *tx_chainmask, + u8 *rx_chainmask, + u8 *antenna_cfgd) +{ + 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->caps.tx_chainmask > + 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 { + ah->diversity_control = settings; + } + + return true; +} + +/*********************/ +/* General Operation */ +/*********************/ + +u32 ath9k_hw_getrxfilter(struct ath_hw *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_hw *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_phy_disable(struct ath_hw *ah) +{ + return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM); +} + +bool ath9k_hw_disable(struct ath_hw *ah) +{ + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + return false; + + return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD); +} + +void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit) +{ + struct ath9k_channel *chan = ah->curchan; + struct ieee80211_channel *channel = chan->chan; + + ah->regulatory.power_limit = min(limit, (u32) MAX_RATE_POWER); + + ah->eep_ops->set_txpower(ah, chan, + ath9k_regd_get_ctl(&ah->regulatory, chan), + channel->max_antenna_gain * 2, + channel->max_power * 2, + min((u32) MAX_RATE_POWER, + (u32) ah->regulatory.power_limit)); +} + +void ath9k_hw_setmac(struct ath_hw *ah, const u8 *mac) +{ + memcpy(ah->macaddr, mac, ETH_ALEN); +} + +void ath9k_hw_setopmode(struct ath_hw *ah) +{ + ath9k_hw_set_operating_mode(ah, ah->opmode); +} + +void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1) +{ + REG_WRITE(ah, AR_MCAST_FIL0, filter0); + REG_WRITE(ah, AR_MCAST_FIL1, filter1); +} + +void ath9k_hw_setbssidmask(struct ath_softc *sc) +{ + REG_WRITE(sc->sc_ah, AR_BSSMSKL, get_unaligned_le32(sc->bssidmask)); + REG_WRITE(sc->sc_ah, AR_BSSMSKU, get_unaligned_le16(sc->bssidmask + 4)); +} + +void ath9k_hw_write_associd(struct ath_softc *sc) +{ + REG_WRITE(sc->sc_ah, AR_BSS_ID0, get_unaligned_le32(sc->curbssid)); + REG_WRITE(sc->sc_ah, AR_BSS_ID1, get_unaligned_le16(sc->curbssid + 4) | + ((sc->curaid & 0x3fff) << AR_BSS_ID1_AID_S)); +} + +u64 ath9k_hw_gettsf64(struct ath_hw *ah) +{ + u64 tsf; + + tsf = REG_READ(ah, AR_TSF_U32); + tsf = (tsf << 32) | REG_READ(ah, AR_TSF_L32); + + return tsf; +} + +void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64) +{ + REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff); + REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff); +} + +void ath9k_hw_reset_tsf(struct ath_hw *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, + "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n"); + break; + } + udelay(10); + } + REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE); +} + +bool ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting) +{ + if (setting) + ah->misc_mode |= AR_PCU_TX_ADD_TSF; + else + ah->misc_mode &= ~AR_PCU_TX_ADD_TSF; + + return true; +} + +bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us) +{ + if (us < ATH9K_SLOT_TIME_9 || us > ath9k_hw_mac_to_usec(ah, 0xffff)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad slot time %u\n", us); + ah->slottime = (u32) -1; + return false; + } else { + REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath9k_hw_mac_to_clks(ah, us)); + ah->slottime = us; + return true; + } +} + +void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode) +{ + u32 macmode; + + if (mode == ATH9K_HT_MACMODE_2040 && + !ah->config.cwm_ignore_extcca) + macmode = AR_2040_JOINED_RX_CLEAR; + else + macmode = 0; + + REG_WRITE(ah, AR_2040_MODE, macmode); +} + +/***************************/ +/* Bluetooth Coexistence */ +/***************************/ + +void ath9k_hw_btcoex_enable(struct ath_hw *ah) +{ + /* connect bt_active to baseband */ + REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, + (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | + AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); + + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); + + /* Set input mux for bt_active to gpio pin */ + REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_BT_ACTIVE, + ah->btactive_gpio); + + /* Configure the desired gpio port for input */ + ath9k_hw_cfg_gpio_input(ah, ah->btactive_gpio); + + /* Configure the desired GPIO port for TX_FRAME output */ + ath9k_hw_cfg_output(ah, ah->wlanactive_gpio, + AR_GPIO_OUTPUT_MUX_AS_TX_FRAME); +} diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h new file mode 100644 index 00000000000..dd8508ef6e0 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2008-2009 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 <linux/if_ether.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include "mac.h" +#include "ani.h" +#include "eeprom.h" +#include "calib.h" +#include "reg.h" +#include "phy.h" + +#include "../regd.h" + +#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 AR9285_DEVID_PCIE 0x002b +#define AR5416_AR9100_DEVID 0x000b +#define AR_SUBVENDOR_ID_NOG 0x0e11 +#define AR_SUBVENDOR_ID_NEW_A 0x7065 +#define AR5416_MAGIC 0x19641014 + +/* Register read/write primitives */ +#define REG_WRITE(_ah, _reg, _val) ath9k_iowrite32((_ah), (_reg), (_val)) +#define REG_READ(_ah, _reg) ath9k_ioread32((_ah), (_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 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 AR_GPIO_OUTPUT_MUX_AS_OUTPUT 0 +#define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1 +#define AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED 2 +#define AR_GPIO_OUTPUT_MUX_AS_TX_FRAME 3 +#define AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED 5 +#define AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED 6 + +#define AR_GPIOD_MASK 0x00001FFF +#define AR_GPIO_BIT(_gpio) (1 << (_gpio)) + +#define BASE_ACTIVATE_DELAY 100 +#define RTC_PLL_SETTLE_DELAY 1000 +#define COEF_SCALE_S 24 +#define HT40_CHANNEL_CENTER_SHIFT 10 + +#define ATH9K_ANTENNA0_CHAINMASK 0x1 +#define ATH9K_ANTENNA1_CHAINMASK 0x2 + +#define ATH9K_NUM_DMA_DEBUG_REGS 8 +#define ATH9K_NUM_QUEUES 10 + +#define MAX_RATE_POWER 63 +#define AH_WAIT_TIMEOUT 100000 /* (us) */ +#define AH_TIME_QUANTUM 10 +#define AR_KEYTABLE_SIZE 128 +#define POWER_UP_TIME 200000 +#define SPUR_RSSI_THRESH 40 + +#define CAB_TIMEOUT_VAL 10 +#define BEACON_TIMEOUT_VAL 10 +#define MIN_BEACON_TIMEOUT_VAL 1 +#define SLEEP_SLOP 3 + +#define INIT_CONFIG_STATUS 0x00000000 +#define INIT_RSSI_THR 0x00000700 +#define INIT_BCON_CNTRL_REG 0x00000000 + +#define TU_TO_USEC(_tu) ((_tu) << 10) + +enum wireless_mode { + ATH9K_MODE_11A = 0, + ATH9K_MODE_11B = 2, + ATH9K_MODE_11G = 3, + ATH9K_MODE_11NA_HT20 = 6, + ATH9K_MODE_11NG_HT20 = 7, + ATH9K_MODE_11NA_HT40PLUS = 8, + ATH9K_MODE_11NA_HT40MINUS = 9, + ATH9K_MODE_11NG_HT40PLUS = 10, + ATH9K_MODE_11NG_HT40MINUS = 11, + ATH9K_MODE_MAX +}; + +enum ath9k_hw_caps { + ATH9K_HW_CAP_MIC_AESCCM = BIT(0), + ATH9K_HW_CAP_MIC_CKIP = BIT(1), + ATH9K_HW_CAP_MIC_TKIP = BIT(2), + ATH9K_HW_CAP_CIPHER_AESCCM = BIT(3), + ATH9K_HW_CAP_CIPHER_CKIP = BIT(4), + ATH9K_HW_CAP_CIPHER_TKIP = BIT(5), + ATH9K_HW_CAP_VEOL = BIT(6), + ATH9K_HW_CAP_BSSIDMASK = BIT(7), + ATH9K_HW_CAP_MCAST_KEYSEARCH = BIT(8), + ATH9K_HW_CAP_HT = BIT(9), + ATH9K_HW_CAP_GTT = BIT(10), + ATH9K_HW_CAP_FASTCC = BIT(11), + ATH9K_HW_CAP_RFSILENT = BIT(12), + ATH9K_HW_CAP_CST = BIT(13), + ATH9K_HW_CAP_ENHANCEDPM = BIT(14), + ATH9K_HW_CAP_AUTOSLEEP = BIT(15), + ATH9K_HW_CAP_4KB_SPLITTRANS = BIT(16), + ATH9K_HW_CAP_BT_COEX = BIT(17) +}; + +enum ath9k_capability_type { + ATH9K_CAP_CIPHER = 0, + ATH9K_CAP_TKIP_MIC, + ATH9K_CAP_TKIP_SPLIT, + ATH9K_CAP_DIVERSITY, + ATH9K_CAP_TXPOW, + ATH9K_CAP_MCAST_KEYSRCH, + ATH9K_CAP_DS +}; + +struct ath9k_hw_capabilities { + u32 hw_caps; /* ATH9K_HW_CAP_* from ath9k_hw_caps */ + DECLARE_BITMAP(wireless_modes, ATH9K_MODE_MAX); /* ATH9K_MODE_* */ + u16 total_queues; + u16 keycache_size; + u16 low_5ghz_chan, high_5ghz_chan; + u16 low_2ghz_chan, high_2ghz_chan; + u16 rts_aggr_limit; + u8 tx_chainmask; + u8 rx_chainmask; + u16 tx_triglevel_max; + u16 reg_cap; + u8 num_gpio_pins; + u8 num_antcfg_2ghz; + u8 num_antcfg_5ghz; +}; + +struct ath9k_ops_config { + int dma_beacon_response_time; + int sw_beacon_response_time; + int additional_swba_backoff; + int ack_6mb; + int cwm_ignore_extcca; + u8 pcie_powersave_enable; + u8 pcie_clock_req; + u32 pcie_waen; + u8 analog_shiftreg; + u8 ht_enable; + u32 ofdm_trig_low; + u32 ofdm_trig_high; + u32 cck_trig_high; + u32 cck_trig_low; + u32 enable_ani; + u16 diversity_control; + u16 antenna_switch_swap; + int serialize_regmode; + bool intr_mitigation; +#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 spurmode; + u16 spurchans[AR_EEPROM_MODAL_SPURS][2]; +}; + +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_TSFOOR = 0x04000000, + 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_TSFOOR | + 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 +}; + +#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_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 { + struct ieee80211_channel *chan; + u16 channel; + u32 channelFlags; + u32 chanmode; + int32_t CalValid; + bool oneTimeCalsDone; + int8_t iCoff; + int8_t qCoff; + int16_t rawNoiseFloor; +}; + +#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_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_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0) +#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0) +#define IS_CHAN_A_5MHZ_SPACED(_c) \ + ((((_c)->channelFlags & CHANNEL_5GHZ) != 0) && \ + (((_c)->channel % 20) != 0) && \ + (((_c)->channel % 10) != 0)) + +/* These macros check chanmode and not channelFlags */ +#define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B) +#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))) + +enum ath9k_power_mode { + ATH9K_PM_AWAKE = 0, + ATH9K_PM_FULL_SLEEP, + ATH9K_PM_NETWORK_SLEEP, + ATH9K_PM_UNDEFINED +}; + +enum ath9k_ant_setting { + ATH9K_ANT_VARIABLE = 0, + ATH9K_ANT_FIXED_A, + ATH9K_ANT_FIXED_B +}; + +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, +}; + +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 +#define ATH9K_TSFOOR_THRESHOLD 0x00004240 /* 16k us */ + u32 bs_dtimperiod; + u16 bs_cfpperiod; + u16 bs_cfpmaxduration; + u32 bs_cfpnext; + u16 bs_timoffset; + u16 bs_bmissthreshold; + u32 bs_sleepduration; + u32 bs_tsfoor_threshold; +}; + +struct chan_centers { + u16 synth_center; + u16 ctl_center; + u16 ext_center; +}; + +enum { + ATH9K_RESET_POWER_ON, + ATH9K_RESET_WARM, + ATH9K_RESET_COLD, +}; + +struct ath9k_hw_version { + u32 magic; + u16 devid; + u16 subvendorid; + u32 macVersion; + u16 macRev; + u16 phyRev; + u16 analog5GhzRev; + u16 analog2GhzRev; +}; + +struct ath_hw { + struct ath_softc *ah_sc; + struct ath9k_hw_version hw_version; + struct ath9k_ops_config config; + struct ath9k_hw_capabilities caps; + struct ath_regulatory regulatory; + struct ath9k_channel channels[38]; + struct ath9k_channel *curchan; + + union { + struct ar5416_eeprom_def def; + struct ar5416_eeprom_4k map4k; + } eeprom; + const struct eeprom_ops *eep_ops; + enum ath9k_eep_map eep_map; + + bool sw_mgmt_crypto; + bool is_pciexpress; + u8 macaddr[ETH_ALEN]; + u16 tx_trig_level; + u16 rfsilent; + u32 rfkill_gpio; + u32 rfkill_polarity; + u32 btactive_gpio; + u32 wlanactive_gpio; + u32 ah_flags; + + enum nl80211_iftype opmode; + enum ath9k_power_mode power_mode; + enum ath9k_power_mode restore_mode; + + struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; + struct ar5416Stats stats; + struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES]; + + int16_t curchan_rad_index; + u32 mask_reg; + u32 txok_interrupt_mask; + u32 txerr_interrupt_mask; + u32 txdesc_interrupt_mask; + u32 txeol_interrupt_mask; + u32 txurn_interrupt_mask; + bool chip_fullsleep; + u32 atim_window; + u16 antenna_switch_swap; + enum ath9k_ant_setting diversity_control; + + /* Calibration */ + enum ath9k_cal_types supp_cals; + struct ath9k_cal_list iq_caldata; + struct ath9k_cal_list adcgain_caldata; + struct ath9k_cal_list adcdc_calinitdata; + struct ath9k_cal_list adcdc_caldata; + struct ath9k_cal_list *cal_list; + struct ath9k_cal_list *cal_list_last; + struct ath9k_cal_list *cal_list_curr; +#define totalPowerMeasI meas0.unsign +#define totalPowerMeasQ meas1.unsign +#define totalIqCorrMeas meas2.sign +#define totalAdcIOddPhase meas0.unsign +#define totalAdcIEvenPhase meas1.unsign +#define totalAdcQOddPhase meas2.unsign +#define totalAdcQEvenPhase meas3.unsign +#define totalAdcDcOffsetIOddPhase meas0.sign +#define totalAdcDcOffsetIEvenPhase meas1.sign +#define totalAdcDcOffsetQOddPhase meas2.sign +#define totalAdcDcOffsetQEvenPhase meas3.sign + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } meas0; + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } meas1; + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } meas2; + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } meas3; + u16 cal_samples; + + u32 sta_id1_defaults; + u32 misc_mode; + enum { + AUTO_32KHZ, + USE_32KHZ, + DONT_USE_32KHZ, + } enable_32kHz_clock; + + /* RF */ + u32 *analogBank0Data; + u32 *analogBank1Data; + u32 *analogBank2Data; + u32 *analogBank3Data; + u32 *analogBank6Data; + u32 *analogBank6TPCData; + u32 *analogBank7Data; + u32 *addac5416_21; + u32 *bank6Temp; + + int16_t txpower_indexoffset; + u32 beacon_interval; + u32 slottime; + u32 acktimeout; + u32 ctstimeout; + u32 globaltxtimeout; + u8 gbeacon_rate; + + /* ANI */ + u32 proc_phyerr; + bool has_hw_phycounters; + u32 aniperiod; + struct ar5416AniState *curani; + struct ar5416AniState ani[255]; + int totalSizeDesired[5]; + int coarse_high[5]; + int coarse_low[5]; + int firpwr[5]; + enum ath9k_ani_cmd ani_function; + + u32 intr_txqs; + enum ath9k_ht_extprotspacing extprotspacing; + u8 txchainmask; + u8 rxchainmask; + + u32 originalGain[22]; + int initPDADC; + int PDADCdelta; + + struct ar5416IniArray iniModes; + struct ar5416IniArray iniCommon; + struct ar5416IniArray iniBank0; + struct ar5416IniArray iniBB_RfGain; + struct ar5416IniArray iniBank1; + struct ar5416IniArray iniBank2; + struct ar5416IniArray iniBank3; + struct ar5416IniArray iniBank6; + struct ar5416IniArray iniBank6TPC; + struct ar5416IniArray iniBank7; + struct ar5416IniArray iniAddac; + struct ar5416IniArray iniPcieSerdes; + struct ar5416IniArray iniModesAdditional; + struct ar5416IniArray iniModesRxGain; + struct ar5416IniArray iniModesTxGain; +}; + +/* Attach, Detach, Reset */ +const char *ath9k_hw_probe(u16 vendorid, u16 devid); +void ath9k_hw_detach(struct ath_hw *ah); +struct ath_hw *ath9k_hw_attach(u16 devid, struct ath_softc *sc, int *error); +void ath9k_hw_rfdetach(struct ath_hw *ah); +int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, + bool bChannelChange); +void ath9k_hw_fill_cap_info(struct ath_hw *ah); +bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type, + u32 capability, u32 *result); +bool ath9k_hw_setcapability(struct ath_hw *ah, enum ath9k_capability_type type, + u32 capability, u32 setting, int *status); + +/* Key Cache Management */ +bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry); +bool ath9k_hw_keysetmac(struct ath_hw *ah, u16 entry, const u8 *mac); +bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, + const struct ath9k_keyval *k, + const u8 *mac); +bool ath9k_hw_keyisvalid(struct ath_hw *ah, u16 entry); + +/* GPIO / RFKILL / Antennae */ +void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio); +u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio); +void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, + u32 ah_signal_type); +void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val); +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) +void ath9k_enable_rfkill(struct ath_hw *ah); +#endif +u32 ath9k_hw_getdefantenna(struct ath_hw *ah); +void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna); +bool ath9k_hw_setantennaswitch(struct ath_hw *ah, + enum ath9k_ant_setting settings, + struct ath9k_channel *chan, + u8 *tx_chainmask, u8 *rx_chainmask, + u8 *antenna_cfgd); + +/* General Operation */ +bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); +u32 ath9k_hw_reverse_bits(u32 val, u32 n); +bool ath9k_get_channel_edges(struct ath_hw *ah, u16 flags, u16 *low, u16 *high); +u16 ath9k_hw_computetxtime(struct ath_hw *ah, + const struct ath_rate_table *rates, + u32 frameLen, u16 rateix, bool shortPreamble); +void ath9k_hw_get_channel_centers(struct ath_hw *ah, + struct ath9k_channel *chan, + struct chan_centers *centers); +u32 ath9k_hw_getrxfilter(struct ath_hw *ah); +void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits); +bool ath9k_hw_phy_disable(struct ath_hw *ah); +bool ath9k_hw_disable(struct ath_hw *ah); +void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit); +void ath9k_hw_setmac(struct ath_hw *ah, const u8 *mac); +void ath9k_hw_setopmode(struct ath_hw *ah); +void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1); +void ath9k_hw_setbssidmask(struct ath_softc *sc); +void ath9k_hw_write_associd(struct ath_softc *sc); +u64 ath9k_hw_gettsf64(struct ath_hw *ah); +void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64); +void ath9k_hw_reset_tsf(struct ath_hw *ah); +bool ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting); +bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us); +void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode); +void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period); +void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, + const struct ath9k_beacon_state *bs); +bool ath9k_hw_setpower(struct ath_hw *ah, + enum ath9k_power_mode mode); +void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore); + +/* Interrupt Handling */ +bool ath9k_hw_intrpend(struct ath_hw *ah); +bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked); +enum ath9k_int ath9k_hw_intrget(struct ath_hw *ah); +enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints); + +void ath9k_hw_btcoex_enable(struct ath_hw *ah); + +#endif diff --git a/drivers/net/wireless/ath/ath9k/initvals.h b/drivers/net/wireless/ath/ath9k/initvals.h new file mode 100644 index 00000000000..e2f0a34b79a --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/initvals.h @@ -0,0 +1,4848 @@ +/* + * Copyright (c) 2008-2009 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[][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, 0x6c48b4e0, 0x6c48b4e0, 0x6c48b0de, 0x6c48b0de, 0x6c48b0de }, + { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e }, + { 0x0000985c, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e }, + { 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, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x000001b8, 0x00000370, 0x00000268, 0x00000134, 0x00000134 }, + { 0x00009924, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b }, + { 0x00009944, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020 }, + { 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[][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, 0xffffffff }, + { 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, 0x00070000 }, + { 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[][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, 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[][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[][2] = { + { 0x0000989c, 0x00000500 }, + { 0x0000989c, 0x00000800 }, + { 0x000098cc, 0x0000000e }, +}; + +static const u32 ar5416Addac[][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 }, + {0x000098cc, 0x00000000 }, +}; + +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, 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_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 }, + { 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, 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, 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_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, 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_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, 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_9100[][2] = { + { 0x0000989c, 0x00000500 }, + { 0x0000989c, 0x00000800 }, + { 0x000098cc, 0x0000000e }, +}; + +static const u32 ar5416Addac_9100[][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, 0x6c48b4e2, 0x6c48b4e2, 0x6c48b0e2, 0x6c48b0e2, 0x6c48b0e2 }, + { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e }, + { 0x0000985c, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e }, + { 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, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009944, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020 }, + { 0x00009960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 }, + { 0x0000a960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 }, + { 0x0000b960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 }, + { 0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, 0x00001120 }, + { 0x0000c968, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce, 0x000003ce }, + { 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, 0xffffffff }, + { 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, 0x00ff0000 }, + { 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 }, + { 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, 0x0000e000 }, + { 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, 0x79bfaa03 }, + { 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 }, +}; + +/* XXX 9280 1 */ +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 }, + { 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 }, + { 0x00008344, 0x00000000 }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xaf268e30 }, + { 0x00009810, 0xfd14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x00009840, 0x206a01ae }, + { 0x0000984c, 0x0040233c }, + { 0x0000a84c, 0x0040233c }, + { 0x00009854, 0x00000044 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x04900000 }, + { 0x0000a920, 0x04900000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280c00a }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0xe250a51e }, + { 0x00009958, 0x3388ffff }, + { 0x00009940, 0x00781204 }, + { 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, 0x006f00c4 }, + { 0x000099b0, 0x03051000 }, + { 0x000099b4, 0x00000820 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000000 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099fc, 0x00001042 }, + { 0x0000a210, 0x4080a333 }, + { 0x0000a214, 0x40206c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x01834061 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x000003b5 }, + { 0x0000a22c, 0x23277200 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c889af }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00007bb6 }, + { 0x0000a248, 0x0fff3ffc }, + { 0x0000a24c, 0x00000001 }, + { 0x0000a250, 0x001da000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cdbd380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0ebae9c6 }, + { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000d270, 0x00820820 }, + { 0x0000a278, 0x1ce739ce }, + { 0x0000a27c, 0x050701ce }, + { 0x0000a358, 0x7999aa0f }, + { 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 }, + { 0x0000a3e4, 0x00000000 }, + { 0x0000a3e8, 0x18c43433 }, + { 0x0000a3ec, 0x00f38081 }, + { 0x00007800, 0x00040000 }, + { 0x00007804, 0xdb005012 }, + { 0x00007808, 0x04924914 }, + { 0x0000780c, 0x21084210 }, + { 0x00007810, 0x6d801300 }, + { 0x00007814, 0x0019beff }, + { 0x00007818, 0x07e40000 }, + { 0x0000781c, 0x00492000 }, + { 0x00007820, 0x92492480 }, + { 0x00007824, 0x00040000 }, + { 0x00007828, 0xdb005012 }, + { 0x0000782c, 0x04924914 }, + { 0x00007830, 0x21084210 }, + { 0x00007834, 0x6d801300 }, + { 0x00007838, 0x0019beff }, + { 0x0000783c, 0x07e40000 }, + { 0x00007840, 0x00492000 }, + { 0x00007844, 0x92492480 }, + { 0x00007848, 0x00120000 }, + { 0x00007850, 0x54214514 }, + { 0x00007858, 0x92592692 }, + { 0x00007860, 0x52802000 }, + { 0x00007864, 0x0a8e370e }, + { 0x00007868, 0xc0102850 }, + { 0x0000786c, 0x812d4000 }, + { 0x00007874, 0x001b6db0 }, + { 0x00007878, 0x00376b63 }, + { 0x0000787c, 0x06db6db6 }, + { 0x00007880, 0x006d8000 }, + { 0x00007884, 0xffeffffe }, + { 0x00007888, 0xffeffffe }, + { 0x00007890, 0x00060aeb }, + { 0x00007894, 0x5a108000 }, + { 0x00007898, 0x2a850160 }, +}; + +/* XXX 9280 2 */ +static const u32 ar9280Modes_9280_2[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f }, + { 0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810, 0x08f04810 }, + { 0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a, 0x0000320a }, + { 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 }, + { 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 }, + { 0x00009840, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e, 0x206a012e }, + { 0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0 }, + { 0x00009850, 0x6c4000e2, 0x6c4000e2, 0x6d4000e2, 0x6c4000e2, 0x6c4000e2 }, + { 0x00009858, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, + { 0x0000985c, 0x31395d5e, 0x31395d5e, 0x3139605e, 0x31395d5e, 0x31395d5e }, + { 0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18 }, + { 0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, + { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a0b, 0xd00a8a0b, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010 }, + { 0x00009960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 }, + { 0x0000a960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 }, + { 0x00009964, 0x00000210, 0x00000210, 0x00000210, 0x00000210, 0x00000210 }, + { 0x000099b8, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c }, + { 0x000099bc, 0x00000a00, 0x00000a00, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, + { 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, 0x00000444, 0x00000444, 0x00000444, 0x00000444, 0x00000444 }, + { 0x0000a20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019 }, + { 0x0000b20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a250, 0x001ff000, 0x001ff000, 0x0004a000, 0x0004a000, 0x0004a000 }, + { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, + { 0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000 }, +}; + +static const u32 ar9280Common_9280_2[][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 }, + { 0x00004060, 0x00000000 }, + { 0x00004064, 0x00000000 }, + { 0x00007010, 0x00000033 }, + { 0x00007034, 0x00000002 }, + { 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, 0x2a80001a }, + { 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 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0xffffffff }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c0, 0x00000000 }, + { 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 }, + { 0x00008264, 0xa8a00010 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x0000829c, 0x00000000 }, + { 0x00008300, 0x00000040 }, + { 0x00008314, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00ff0000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00008344, 0x00581043 }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xafa68e30 }, + { 0x00009810, 0xfd14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x0000984c, 0x0040233c }, + { 0x0000a84c, 0x0040233c }, + { 0x00009854, 0x00000044 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x00009910, 0x01002310 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x04900000 }, + { 0x0000a920, 0x04900000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280c00a }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5f3ca3de }, + { 0x00009958, 0x2108ecff }, + { 0x00009940, 0x14750604 }, + { 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 }, + { 0x000099b4, 0x00000820 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000000 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099f0, 0x00000000 }, + { 0x000099fc, 0x00001042 }, + { 0x0000a208, 0x803e4788 }, + { 0x0000a210, 0x4080a333 }, + { 0x0000a214, 0x40206c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x01834061 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x000003b5 }, + { 0x0000a22c, 0x233f7180 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c88000 }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00007bb6 }, + { 0x0000a248, 0x0fff3ffc }, + { 0x0000a24c, 0x00000000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cdbd380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0ebae9c6 }, + { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000d270, 0x00820820 }, + { 0x0000a278, 0x1ce739ce }, + { 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 }, + { 0x0000a3e4, 0x00000000 }, + { 0x0000a3e8, 0x18c43433 }, + { 0x0000a3ec, 0x00f70081 }, + { 0x00007800, 0x00040000 }, + { 0x00007804, 0xdb005012 }, + { 0x00007808, 0x04924914 }, + { 0x0000780c, 0x21084210 }, + { 0x00007810, 0x6d801300 }, + { 0x00007818, 0x07e41000 }, + { 0x00007824, 0x00040000 }, + { 0x00007828, 0xdb005012 }, + { 0x0000782c, 0x04924914 }, + { 0x00007830, 0x21084210 }, + { 0x00007834, 0x6d801300 }, + { 0x0000783c, 0x07e40000 }, + { 0x00007848, 0x00100000 }, + { 0x0000784c, 0x773f0567 }, + { 0x00007850, 0x54214514 }, + { 0x00007854, 0x12035828 }, + { 0x00007858, 0x9259269a }, + { 0x00007860, 0x52802000 }, + { 0x00007864, 0x0a8e370e }, + { 0x00007868, 0xc0102850 }, + { 0x0000786c, 0x812d4000 }, + { 0x00007870, 0x807ec400 }, + { 0x00007874, 0x001b6db0 }, + { 0x00007878, 0x00376b63 }, + { 0x0000787c, 0x06db6db6 }, + { 0x00007880, 0x006d8000 }, + { 0x00007884, 0xffeffffe }, + { 0x00007888, 0xffeffffe }, + { 0x0000788c, 0x00010000 }, + { 0x00007890, 0x02060aeb }, + { 0x00007898, 0x2a850160 }, +}; + +static const u32 ar9280Modes_fast_clock_9280_2[][3] = { + { 0x00001030, 0x00000268, 0x000004d0 }, + { 0x00001070, 0x0000018c, 0x00000318 }, + { 0x000010b0, 0x00000fd0, 0x00001fa0 }, + { 0x00008014, 0x044c044c, 0x08980898 }, + { 0x0000801c, 0x148ec02b, 0x148ec057 }, + { 0x00008318, 0x000044c0, 0x00008980 }, + { 0x00009820, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000f0f, 0x00000f0f }, + { 0x00009828, 0x0b020001, 0x0b020001 }, + { 0x00009834, 0x00000f0f, 0x00000f0f }, + { 0x00009844, 0x03721821, 0x03721821 }, + { 0x00009914, 0x00000898, 0x00001130 }, + { 0x00009918, 0x0000000b, 0x00000016 }, +}; + +static const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][6] = { + { 0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290 }, + { 0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300 }, + { 0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304 }, + { 0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308, 0x00000308 }, + { 0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c, 0x0000030c }, + { 0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000, 0x00008000 }, + { 0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004, 0x00008004 }, + { 0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008, 0x00008008 }, + { 0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c, 0x0000800c }, + { 0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080, 0x00008080 }, + { 0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084, 0x00008084 }, + { 0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088, 0x00008088 }, + { 0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c, 0x0000808c }, + { 0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100, 0x00008100 }, + { 0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104, 0x00008104 }, + { 0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108, 0x00008108 }, + { 0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c, 0x0000810c }, + { 0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110, 0x00008110 }, + { 0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114, 0x00008114 }, + { 0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180, 0x00008180 }, + { 0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184, 0x00008184 }, + { 0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188, 0x00008188 }, + { 0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c, 0x0000818c }, + { 0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190, 0x00008190 }, + { 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, 0x00008b10, 0x00008b10, 0x00008b10 }, + { 0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b14, 0x00008b14, 0x00008b14 }, + { 0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b01, 0x00008b01, 0x00008b01 }, + { 0x00009acc, 0x0000b380, 0x0000b380, 0x00008b05, 0x00008b05, 0x00008b05 }, + { 0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b09, 0x00008b09, 0x00008b09 }, + { 0x00009ad4, 0x0000b388, 0x0000b388, 0x00008b0d, 0x00008b0d, 0x00008b0d }, + { 0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008b11, 0x00008b11, 0x00008b11 }, + { 0x00009adc, 0x0000b390, 0x0000b390, 0x00008b15, 0x00008b15, 0x00008b15 }, + { 0x00009ae0, 0x0000b394, 0x0000b394, 0x00008b02, 0x00008b02, 0x00008b02 }, + { 0x00009ae4, 0x0000b398, 0x0000b398, 0x00008b06, 0x00008b06, 0x00008b06 }, + { 0x00009ae8, 0x0000b780, 0x0000b780, 0x00008b0a, 0x00008b0a, 0x00008b0a }, + { 0x00009aec, 0x0000b784, 0x0000b784, 0x00008b0e, 0x00008b0e, 0x00008b0e }, + { 0x00009af0, 0x0000b788, 0x0000b788, 0x00008b12, 0x00008b12, 0x00008b12 }, + { 0x00009af4, 0x0000b78c, 0x0000b78c, 0x00008b16, 0x00008b16, 0x00008b16 }, + { 0x00009af8, 0x0000b790, 0x0000b790, 0x00008b03, 0x00008b03, 0x00008b03 }, + { 0x00009afc, 0x0000b794, 0x0000b794, 0x00008b07, 0x00008b07, 0x00008b07 }, + { 0x00009b00, 0x0000b798, 0x0000b798, 0x00008b0b, 0x00008b0b, 0x00008b0b }, + { 0x00009b04, 0x0000d784, 0x0000d784, 0x00008b0f, 0x00008b0f, 0x00008b0f }, + { 0x00009b08, 0x0000d788, 0x0000d788, 0x00008b13, 0x00008b13, 0x00008b13 }, + { 0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00008b17, 0x00008b17, 0x00008b17 }, + { 0x00009b10, 0x0000d790, 0x0000d790, 0x00008b23, 0x00008b23, 0x00008b23 }, + { 0x00009b14, 0x0000f780, 0x0000f780, 0x00008b27, 0x00008b27, 0x00008b27 }, + { 0x00009b18, 0x0000f784, 0x0000f784, 0x00008b2b, 0x00008b2b, 0x00008b2b }, + { 0x00009b1c, 0x0000f788, 0x0000f788, 0x00008b2f, 0x00008b2f, 0x00008b2f }, + { 0x00009b20, 0x0000f78c, 0x0000f78c, 0x00008b33, 0x00008b33, 0x00008b33 }, + { 0x00009b24, 0x0000f790, 0x0000f790, 0x00008b37, 0x00008b37, 0x00008b37 }, + { 0x00009b28, 0x0000f794, 0x0000f794, 0x00008b43, 0x00008b43, 0x00008b43 }, + { 0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x00008b47, 0x00008b47, 0x00008b47 }, + { 0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x00008b4b, 0x00008b4b, 0x00008b4b }, + { 0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x00008b4f, 0x00008b4f, 0x00008b4f }, + { 0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x00008b53, 0x00008b53, 0x00008b53 }, + { 0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x00008b57, 0x00008b57, 0x00008b57 }, + { 0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b98, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009b9c, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009ba0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009ba4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009ba8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bac, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bb0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bb4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bb8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bbc, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bc0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bc4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bc8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bcc, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bd0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bd4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bd8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bdc, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009be0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009be4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009be8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bec, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bf0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bf4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bf8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009bfc, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b }, + { 0x00009848, 0x00001066, 0x00001066, 0x00001050, 0x00001050, 0x00001050 }, + { 0x0000a848, 0x00001066, 0x00001066, 0x00001050, 0x00001050, 0x00001050 }, +}; + +static const u32 ar9280Modes_original_rxgain_9280_2[][6] = { + { 0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290 }, + { 0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300 }, + { 0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304 }, + { 0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308, 0x00000308 }, + { 0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c, 0x0000030c }, + { 0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000, 0x00008000 }, + { 0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004, 0x00008004 }, + { 0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008, 0x00008008 }, + { 0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c, 0x0000800c }, + { 0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080, 0x00008080 }, + { 0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084, 0x00008084 }, + { 0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088, 0x00008088 }, + { 0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c, 0x0000808c }, + { 0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100, 0x00008100 }, + { 0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104, 0x00008104 }, + { 0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108, 0x00008108 }, + { 0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c, 0x0000810c }, + { 0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110, 0x00008110 }, + { 0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114, 0x00008114 }, + { 0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180, 0x00008180 }, + { 0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184, 0x00008184 }, + { 0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188, 0x00008188 }, + { 0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c, 0x0000818c }, + { 0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190, 0x00008190 }, + { 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 }, + { 0x00009848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063 }, + { 0x0000a848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063 }, +}; + +static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = { + { 0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290 }, + { 0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300 }, + { 0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304 }, + { 0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308, 0x00000308 }, + { 0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c, 0x0000030c }, + { 0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000, 0x00008000 }, + { 0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004, 0x00008004 }, + { 0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008, 0x00008008 }, + { 0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c, 0x0000800c }, + { 0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080, 0x00008080 }, + { 0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084, 0x00008084 }, + { 0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088, 0x00008088 }, + { 0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c, 0x0000808c }, + { 0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100, 0x00008100 }, + { 0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104, 0x00008104 }, + { 0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108, 0x00008108 }, + { 0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c, 0x0000810c }, + { 0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110, 0x00008110 }, + { 0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114, 0x00008114 }, + { 0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180, 0x00008180 }, + { 0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184, 0x00008184 }, + { 0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188, 0x00008188 }, + { 0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c, 0x0000818c }, + { 0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190, 0x00008190 }, + { 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, 0x00009310, 0x00009310, 0x00009310 }, + { 0x00009aec, 0x0000b784, 0x0000b784, 0x00009314, 0x00009314, 0x00009314 }, + { 0x00009af0, 0x0000b788, 0x0000b788, 0x00009320, 0x00009320, 0x00009320 }, + { 0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009324, 0x00009324, 0x00009324 }, + { 0x00009af8, 0x0000b790, 0x0000b790, 0x00009328, 0x00009328, 0x00009328 }, + { 0x00009afc, 0x0000b794, 0x0000b794, 0x0000932c, 0x0000932c, 0x0000932c }, + { 0x00009b00, 0x0000b798, 0x0000b798, 0x00009330, 0x00009330, 0x00009330 }, + { 0x00009b04, 0x0000d784, 0x0000d784, 0x00009334, 0x00009334, 0x00009334 }, + { 0x00009b08, 0x0000d788, 0x0000d788, 0x00009321, 0x00009321, 0x00009321 }, + { 0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009325, 0x00009325, 0x00009325 }, + { 0x00009b10, 0x0000d790, 0x0000d790, 0x00009329, 0x00009329, 0x00009329 }, + { 0x00009b14, 0x0000f780, 0x0000f780, 0x0000932d, 0x0000932d, 0x0000932d }, + { 0x00009b18, 0x0000f784, 0x0000f784, 0x00009331, 0x00009331, 0x00009331 }, + { 0x00009b1c, 0x0000f788, 0x0000f788, 0x00009335, 0x00009335, 0x00009335 }, + { 0x00009b20, 0x0000f78c, 0x0000f78c, 0x00009322, 0x00009322, 0x00009322 }, + { 0x00009b24, 0x0000f790, 0x0000f790, 0x00009326, 0x00009326, 0x00009326 }, + { 0x00009b28, 0x0000f794, 0x0000f794, 0x0000932a, 0x0000932a, 0x0000932a }, + { 0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x0000932e, 0x0000932e, 0x0000932e }, + { 0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x00009332, 0x00009332, 0x00009332 }, + { 0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x00009336, 0x00009336, 0x00009336 }, + { 0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x00009323, 0x00009323, 0x00009323 }, + { 0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x00009327, 0x00009327, 0x00009327 }, + { 0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x0000932b, 0x0000932b, 0x0000932b }, + { 0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x0000932f, 0x0000932f, 0x0000932f }, + { 0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x00009333, 0x00009333, 0x00009333 }, + { 0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x00009337, 0x00009337, 0x00009337 }, + { 0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x00009343, 0x00009343, 0x00009343 }, + { 0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x00009347, 0x00009347, 0x00009347 }, + { 0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x0000934b, 0x0000934b, 0x0000934b }, + { 0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x0000934f, 0x0000934f, 0x0000934f }, + { 0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x00009353, 0x00009353, 0x00009353 }, + { 0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x00009357, 0x00009357, 0x00009357 }, + { 0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b98, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009b9c, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009ba0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009ba4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009ba8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bac, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bb0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bb4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bb8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bbc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bc0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bc4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bc8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bcc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bd0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bd4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bd8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bdc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009be0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009be4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009be8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bec, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bf0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bf4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bf8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009bfc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b }, + { 0x00009848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a, 0x0000105a }, + { 0x0000a848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a, 0x0000105a }, +}; + +static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = { + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00003002, 0x00003002, 0x00004002, 0x00004002, 0x00004002 }, + { 0x0000a308, 0x00006004, 0x00006004, 0x00007008, 0x00007008, 0x00007008 }, + { 0x0000a30c, 0x0000a006, 0x0000a006, 0x0000c010, 0x0000c010, 0x0000c010 }, + { 0x0000a310, 0x0000e012, 0x0000e012, 0x00010012, 0x00010012, 0x00010012 }, + { 0x0000a314, 0x00011014, 0x00011014, 0x00013014, 0x00013014, 0x00013014 }, + { 0x0000a318, 0x0001504a, 0x0001504a, 0x0001820a, 0x0001820a, 0x0001820a }, + { 0x0000a31c, 0x0001904c, 0x0001904c, 0x0001b211, 0x0001b211, 0x0001b211 }, + { 0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213, 0x0001e213 }, + { 0x0000a324, 0x00021092, 0x00021092, 0x00022411, 0x00022411, 0x00022411 }, + { 0x0000a328, 0x0002510a, 0x0002510a, 0x00025413, 0x00025413, 0x00025413 }, + { 0x0000a32c, 0x0002910c, 0x0002910c, 0x00029811, 0x00029811, 0x00029811 }, + { 0x0000a330, 0x0002c18b, 0x0002c18b, 0x0002c813, 0x0002c813, 0x0002c813 }, + { 0x0000a334, 0x0002f1cc, 0x0002f1cc, 0x00030a14, 0x00030a14, 0x00030a14 }, + { 0x0000a338, 0x000321eb, 0x000321eb, 0x00035a50, 0x00035a50, 0x00035a50 }, + { 0x0000a33c, 0x000341ec, 0x000341ec, 0x00039c4c, 0x00039c4c, 0x00039c4c }, + { 0x0000a340, 0x000341ec, 0x000341ec, 0x0003de8a, 0x0003de8a, 0x0003de8a }, + { 0x0000a344, 0x000341ec, 0x000341ec, 0x00042e92, 0x00042e92, 0x00042e92 }, + { 0x0000a348, 0x000341ec, 0x000341ec, 0x00046ed2, 0x00046ed2, 0x00046ed2 }, + { 0x0000a34c, 0x000341ec, 0x000341ec, 0x0004bed5, 0x0004bed5, 0x0004bed5 }, + { 0x0000a350, 0x000341ec, 0x000341ec, 0x0004ff54, 0x0004ff54, 0x0004ff54 }, + { 0x0000a354, 0x000341ec, 0x000341ec, 0x00055fd5, 0x00055fd5, 0x00055fd5 }, + { 0x00007814, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff }, + { 0x00007838, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff }, + { 0x0000781c, 0x00172000, 0x00172000, 0x00172000, 0x00172000, 0x00172000 }, + { 0x00007840, 0x00172000, 0x00172000, 0x00172000, 0x00172000, 0x00172000 }, + { 0x00007820, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480 }, + { 0x00007844, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480 }, + { 0x0000a274, 0x0a19e652, 0x0a19e652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 }, + { 0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce }, +}; + +static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = { + { 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 }, + { 0x00007814, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff }, + { 0x00007838, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff }, + { 0x0000781c, 0x00392000, 0x00392000, 0x00392000, 0x00392000, 0x00392000 }, + { 0x00007840, 0x00392000, 0x00392000, 0x00392000, 0x00392000, 0x00392000 }, + { 0x00007820, 0x92592480, 0x92592480, 0x92592480, 0x92592480, 0x92592480 }, + { 0x00007844, 0x92592480, 0x92592480, 0x92592480, 0x92592480, 0x92592480 }, + { 0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 }, + { 0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce }, +}; + +static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0xc01dcffc }, + {0x00004040, 0x1aaabe41 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; + +static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0xc01dcffd }, + {0x00004040, 0x1aaabe41 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; + +/* AR9285 */ +static const u_int32_t ar9285Modes_9285[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f }, + { 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 }, + { 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 }, + { 0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e }, + { 0x00009844, 0x0372161e, 0x0372161e, 0x03720020, 0x03720020, 0x037216a0 }, + { 0x00009848, 0x00001066, 0x00001066, 0x0000004e, 0x0000004e, 0x00001059 }, + { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 }, + { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3136605e, 0x3136605e, 0x3139605e }, + { 0x00009860, 0x00058d18, 0x00058d18, 0x00058d20, 0x00058d20, 0x00058d18 }, + { 0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, + { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d }, + { 0x00009944, 0xdfbc1010, 0xdfbc1010, 0xdfbc1020, 0xdfbc1020, 0xdfbc1010 }, + { 0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099b8, 0x00cf4d1c, 0x00cf4d1c, 0x00cf4d1c, 0x00cf4d1c, 0x00cf4d1c }, + { 0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, + { 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 }, + { 0x00009a00, 0x00000000, 0x00000000, 0x00068084, 0x00068084, 0x00000000 }, + { 0x00009a04, 0x00000000, 0x00000000, 0x00068088, 0x00068088, 0x00000000 }, + { 0x00009a08, 0x00000000, 0x00000000, 0x0006808c, 0x0006808c, 0x00000000 }, + { 0x00009a0c, 0x00000000, 0x00000000, 0x00068100, 0x00068100, 0x00000000 }, + { 0x00009a10, 0x00000000, 0x00000000, 0x00068104, 0x00068104, 0x00000000 }, + { 0x00009a14, 0x00000000, 0x00000000, 0x00068108, 0x00068108, 0x00000000 }, + { 0x00009a18, 0x00000000, 0x00000000, 0x0006810c, 0x0006810c, 0x00000000 }, + { 0x00009a1c, 0x00000000, 0x00000000, 0x00068110, 0x00068110, 0x00000000 }, + { 0x00009a20, 0x00000000, 0x00000000, 0x00068114, 0x00068114, 0x00000000 }, + { 0x00009a24, 0x00000000, 0x00000000, 0x00068180, 0x00068180, 0x00000000 }, + { 0x00009a28, 0x00000000, 0x00000000, 0x00068184, 0x00068184, 0x00000000 }, + { 0x00009a2c, 0x00000000, 0x00000000, 0x00068188, 0x00068188, 0x00000000 }, + { 0x00009a30, 0x00000000, 0x00000000, 0x0006818c, 0x0006818c, 0x00000000 }, + { 0x00009a34, 0x00000000, 0x00000000, 0x00068190, 0x00068190, 0x00000000 }, + { 0x00009a38, 0x00000000, 0x00000000, 0x00068194, 0x00068194, 0x00000000 }, + { 0x00009a3c, 0x00000000, 0x00000000, 0x000681a0, 0x000681a0, 0x00000000 }, + { 0x00009a40, 0x00000000, 0x00000000, 0x0006820c, 0x0006820c, 0x00000000 }, + { 0x00009a44, 0x00000000, 0x00000000, 0x000681a8, 0x000681a8, 0x00000000 }, + { 0x00009a48, 0x00000000, 0x00000000, 0x00068284, 0x00068284, 0x00000000 }, + { 0x00009a4c, 0x00000000, 0x00000000, 0x00068288, 0x00068288, 0x00000000 }, + { 0x00009a50, 0x00000000, 0x00000000, 0x00068220, 0x00068220, 0x00000000 }, + { 0x00009a54, 0x00000000, 0x00000000, 0x00068290, 0x00068290, 0x00000000 }, + { 0x00009a58, 0x00000000, 0x00000000, 0x00068300, 0x00068300, 0x00000000 }, + { 0x00009a5c, 0x00000000, 0x00000000, 0x00068304, 0x00068304, 0x00000000 }, + { 0x00009a60, 0x00000000, 0x00000000, 0x00068308, 0x00068308, 0x00000000 }, + { 0x00009a64, 0x00000000, 0x00000000, 0x0006830c, 0x0006830c, 0x00000000 }, + { 0x00009a68, 0x00000000, 0x00000000, 0x00068380, 0x00068380, 0x00000000 }, + { 0x00009a6c, 0x00000000, 0x00000000, 0x00068384, 0x00068384, 0x00000000 }, + { 0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000 }, + { 0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000 }, + { 0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000 }, + { 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 }, + { 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, + { 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, + { 0x00009a88, 0x00000000, 0x00000000, 0x00068b04, 0x00068b04, 0x00000000 }, + { 0x00009a8c, 0x00000000, 0x00000000, 0x00068b08, 0x00068b08, 0x00000000 }, + { 0x00009a90, 0x00000000, 0x00000000, 0x00068b08, 0x00068b08, 0x00000000 }, + { 0x00009a94, 0x00000000, 0x00000000, 0x00068b0c, 0x00068b0c, 0x00000000 }, + { 0x00009a98, 0x00000000, 0x00000000, 0x00068b80, 0x00068b80, 0x00000000 }, + { 0x00009a9c, 0x00000000, 0x00000000, 0x00068b84, 0x00068b84, 0x00000000 }, + { 0x00009aa0, 0x00000000, 0x00000000, 0x00068b88, 0x00068b88, 0x00000000 }, + { 0x00009aa4, 0x00000000, 0x00000000, 0x00068b8c, 0x00068b8c, 0x00000000 }, + { 0x00009aa8, 0x00000000, 0x00000000, 0x000b8b90, 0x000b8b90, 0x00000000 }, + { 0x00009aac, 0x00000000, 0x00000000, 0x000b8f80, 0x000b8f80, 0x00000000 }, + { 0x00009ab0, 0x00000000, 0x00000000, 0x000b8f84, 0x000b8f84, 0x00000000 }, + { 0x00009ab4, 0x00000000, 0x00000000, 0x000b8f88, 0x000b8f88, 0x00000000 }, + { 0x00009ab8, 0x00000000, 0x00000000, 0x000b8f8c, 0x000b8f8c, 0x00000000 }, + { 0x00009abc, 0x00000000, 0x00000000, 0x000b8f90, 0x000b8f90, 0x00000000 }, + { 0x00009ac0, 0x00000000, 0x00000000, 0x000bb30c, 0x000bb30c, 0x00000000 }, + { 0x00009ac4, 0x00000000, 0x00000000, 0x000bb310, 0x000bb310, 0x00000000 }, + { 0x00009ac8, 0x00000000, 0x00000000, 0x000bb384, 0x000bb384, 0x00000000 }, + { 0x00009acc, 0x00000000, 0x00000000, 0x000bb388, 0x000bb388, 0x00000000 }, + { 0x00009ad0, 0x00000000, 0x00000000, 0x000bb324, 0x000bb324, 0x00000000 }, + { 0x00009ad4, 0x00000000, 0x00000000, 0x000bb704, 0x000bb704, 0x00000000 }, + { 0x00009ad8, 0x00000000, 0x00000000, 0x000f96a4, 0x000f96a4, 0x00000000 }, + { 0x00009adc, 0x00000000, 0x00000000, 0x000f96a8, 0x000f96a8, 0x00000000 }, + { 0x00009ae0, 0x00000000, 0x00000000, 0x000f9710, 0x000f9710, 0x00000000 }, + { 0x00009ae4, 0x00000000, 0x00000000, 0x000f9714, 0x000f9714, 0x00000000 }, + { 0x00009ae8, 0x00000000, 0x00000000, 0x000f9720, 0x000f9720, 0x00000000 }, + { 0x00009aec, 0x00000000, 0x00000000, 0x000f9724, 0x000f9724, 0x00000000 }, + { 0x00009af0, 0x00000000, 0x00000000, 0x000f9728, 0x000f9728, 0x00000000 }, + { 0x00009af4, 0x00000000, 0x00000000, 0x000f972c, 0x000f972c, 0x00000000 }, + { 0x00009af8, 0x00000000, 0x00000000, 0x000f97a0, 0x000f97a0, 0x00000000 }, + { 0x00009afc, 0x00000000, 0x00000000, 0x000f97a4, 0x000f97a4, 0x00000000 }, + { 0x00009b00, 0x00000000, 0x00000000, 0x000fb7a8, 0x000fb7a8, 0x00000000 }, + { 0x00009b04, 0x00000000, 0x00000000, 0x000fb7b0, 0x000fb7b0, 0x00000000 }, + { 0x00009b08, 0x00000000, 0x00000000, 0x000fb7b4, 0x000fb7b4, 0x00000000 }, + { 0x00009b0c, 0x00000000, 0x00000000, 0x000fb7b8, 0x000fb7b8, 0x00000000 }, + { 0x00009b10, 0x00000000, 0x00000000, 0x000fb7a5, 0x000fb7a5, 0x00000000 }, + { 0x00009b14, 0x00000000, 0x00000000, 0x000fb7a9, 0x000fb7a9, 0x00000000 }, + { 0x00009b18, 0x00000000, 0x00000000, 0x000fb7ad, 0x000fb7ad, 0x00000000 }, + { 0x00009b1c, 0x00000000, 0x00000000, 0x000fb7b1, 0x000fb7b1, 0x00000000 }, + { 0x00009b20, 0x00000000, 0x00000000, 0x000fb7b5, 0x000fb7b5, 0x00000000 }, + { 0x00009b24, 0x00000000, 0x00000000, 0x000fb7b9, 0x000fb7b9, 0x00000000 }, + { 0x00009b28, 0x00000000, 0x00000000, 0x000fb7c5, 0x000fb7c5, 0x00000000 }, + { 0x00009b2c, 0x00000000, 0x00000000, 0x000fb7c9, 0x000fb7c9, 0x00000000 }, + { 0x00009b30, 0x00000000, 0x00000000, 0x000fb7d1, 0x000fb7d1, 0x00000000 }, + { 0x00009b34, 0x00000000, 0x00000000, 0x000fb7d5, 0x000fb7d5, 0x00000000 }, + { 0x00009b38, 0x00000000, 0x00000000, 0x000fb7d9, 0x000fb7d9, 0x00000000 }, + { 0x00009b3c, 0x00000000, 0x00000000, 0x000fb7c6, 0x000fb7c6, 0x00000000 }, + { 0x00009b40, 0x00000000, 0x00000000, 0x000fb7ca, 0x000fb7ca, 0x00000000 }, + { 0x00009b44, 0x00000000, 0x00000000, 0x000fb7ce, 0x000fb7ce, 0x00000000 }, + { 0x00009b48, 0x00000000, 0x00000000, 0x000fb7d2, 0x000fb7d2, 0x00000000 }, + { 0x00009b4c, 0x00000000, 0x00000000, 0x000fb7d6, 0x000fb7d6, 0x00000000 }, + { 0x00009b50, 0x00000000, 0x00000000, 0x000fb7c3, 0x000fb7c3, 0x00000000 }, + { 0x00009b54, 0x00000000, 0x00000000, 0x000fb7c7, 0x000fb7c7, 0x00000000 }, + { 0x00009b58, 0x00000000, 0x00000000, 0x000fb7cb, 0x000fb7cb, 0x00000000 }, + { 0x00009b5c, 0x00000000, 0x00000000, 0x000fb7cf, 0x000fb7cf, 0x00000000 }, + { 0x00009b60, 0x00000000, 0x00000000, 0x000fb7d7, 0x000fb7d7, 0x00000000 }, + { 0x00009b64, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b68, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b6c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b70, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b74, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b78, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b7c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b80, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b84, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b88, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b8c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b90, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b94, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b98, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009b9c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009ba0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009ba4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009ba8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bac, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bb0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bb4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bb8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bbc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bc0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bc4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bc8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bcc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bd0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bd4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bd8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bdc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009be0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009be4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009be8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bec, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bf0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bf4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bf8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x00009bfc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 }, + { 0x0000aa00, 0x00000000, 0x00000000, 0x0006801c, 0x0006801c, 0x00000000 }, + { 0x0000aa04, 0x00000000, 0x00000000, 0x00068080, 0x00068080, 0x00000000 }, + { 0x0000aa08, 0x00000000, 0x00000000, 0x00068084, 0x00068084, 0x00000000 }, + { 0x0000aa0c, 0x00000000, 0x00000000, 0x00068088, 0x00068088, 0x00000000 }, + { 0x0000aa10, 0x00000000, 0x00000000, 0x0006808c, 0x0006808c, 0x00000000 }, + { 0x0000aa14, 0x00000000, 0x00000000, 0x00068100, 0x00068100, 0x00000000 }, + { 0x0000aa18, 0x00000000, 0x00000000, 0x00068104, 0x00068104, 0x00000000 }, + { 0x0000aa1c, 0x00000000, 0x00000000, 0x00068108, 0x00068108, 0x00000000 }, + { 0x0000aa20, 0x00000000, 0x00000000, 0x0006810c, 0x0006810c, 0x00000000 }, + { 0x0000aa24, 0x00000000, 0x00000000, 0x00068110, 0x00068110, 0x00000000 }, + { 0x0000aa28, 0x00000000, 0x00000000, 0x00068110, 0x00068110, 0x00000000 }, + { 0x0000aa2c, 0x00000000, 0x00000000, 0x00068180, 0x00068180, 0x00000000 }, + { 0x0000aa30, 0x00000000, 0x00000000, 0x00068184, 0x00068184, 0x00000000 }, + { 0x0000aa34, 0x00000000, 0x00000000, 0x00068188, 0x00068188, 0x00000000 }, + { 0x0000aa38, 0x00000000, 0x00000000, 0x0006818c, 0x0006818c, 0x00000000 }, + { 0x0000aa3c, 0x00000000, 0x00000000, 0x00068190, 0x00068190, 0x00000000 }, + { 0x0000aa40, 0x00000000, 0x00000000, 0x00068194, 0x00068194, 0x00000000 }, + { 0x0000aa44, 0x00000000, 0x00000000, 0x000681a0, 0x000681a0, 0x00000000 }, + { 0x0000aa48, 0x00000000, 0x00000000, 0x0006820c, 0x0006820c, 0x00000000 }, + { 0x0000aa4c, 0x00000000, 0x00000000, 0x000681a8, 0x000681a8, 0x00000000 }, + { 0x0000aa50, 0x00000000, 0x00000000, 0x000681ac, 0x000681ac, 0x00000000 }, + { 0x0000aa54, 0x00000000, 0x00000000, 0x0006821c, 0x0006821c, 0x00000000 }, + { 0x0000aa58, 0x00000000, 0x00000000, 0x00068224, 0x00068224, 0x00000000 }, + { 0x0000aa5c, 0x00000000, 0x00000000, 0x00068290, 0x00068290, 0x00000000 }, + { 0x0000aa60, 0x00000000, 0x00000000, 0x00068300, 0x00068300, 0x00000000 }, + { 0x0000aa64, 0x00000000, 0x00000000, 0x00068308, 0x00068308, 0x00000000 }, + { 0x0000aa68, 0x00000000, 0x00000000, 0x0006830c, 0x0006830c, 0x00000000 }, + { 0x0000aa6c, 0x00000000, 0x00000000, 0x00068310, 0x00068310, 0x00000000 }, + { 0x0000aa70, 0x00000000, 0x00000000, 0x00068788, 0x00068788, 0x00000000 }, + { 0x0000aa74, 0x00000000, 0x00000000, 0x0006878c, 0x0006878c, 0x00000000 }, + { 0x0000aa78, 0x00000000, 0x00000000, 0x00068790, 0x00068790, 0x00000000 }, + { 0x0000aa7c, 0x00000000, 0x00000000, 0x00068794, 0x00068794, 0x00000000 }, + { 0x0000aa80, 0x00000000, 0x00000000, 0x00068798, 0x00068798, 0x00000000 }, + { 0x0000aa84, 0x00000000, 0x00000000, 0x0006879c, 0x0006879c, 0x00000000 }, + { 0x0000aa88, 0x00000000, 0x00000000, 0x00068b89, 0x00068b89, 0x00000000 }, + { 0x0000aa8c, 0x00000000, 0x00000000, 0x00068b8d, 0x00068b8d, 0x00000000 }, + { 0x0000aa90, 0x00000000, 0x00000000, 0x00068b91, 0x00068b91, 0x00000000 }, + { 0x0000aa94, 0x00000000, 0x00000000, 0x00068b95, 0x00068b95, 0x00000000 }, + { 0x0000aa98, 0x00000000, 0x00000000, 0x00068b99, 0x00068b99, 0x00000000 }, + { 0x0000aa9c, 0x00000000, 0x00000000, 0x00068ba5, 0x00068ba5, 0x00000000 }, + { 0x0000aaa0, 0x00000000, 0x00000000, 0x00068ba9, 0x00068ba9, 0x00000000 }, + { 0x0000aaa4, 0x00000000, 0x00000000, 0x00068bad, 0x00068bad, 0x00000000 }, + { 0x0000aaa8, 0x00000000, 0x00000000, 0x000b8b0c, 0x000b8b0c, 0x00000000 }, + { 0x0000aaac, 0x00000000, 0x00000000, 0x000b8f10, 0x000b8f10, 0x00000000 }, + { 0x0000aab0, 0x00000000, 0x00000000, 0x000b8f14, 0x000b8f14, 0x00000000 }, + { 0x0000aab4, 0x00000000, 0x00000000, 0x000b8f84, 0x000b8f84, 0x00000000 }, + { 0x0000aab8, 0x00000000, 0x00000000, 0x000b8f84, 0x000b8f84, 0x00000000 }, + { 0x0000aabc, 0x00000000, 0x00000000, 0x000b8f88, 0x000b8f88, 0x00000000 }, + { 0x0000aac0, 0x00000000, 0x00000000, 0x000bb380, 0x000bb380, 0x00000000 }, + { 0x0000aac4, 0x00000000, 0x00000000, 0x000bb384, 0x000bb384, 0x00000000 }, + { 0x0000aac8, 0x00000000, 0x00000000, 0x000bb388, 0x000bb388, 0x00000000 }, + { 0x0000aacc, 0x00000000, 0x00000000, 0x000bb38c, 0x000bb38c, 0x00000000 }, + { 0x0000aad0, 0x00000000, 0x00000000, 0x000bb394, 0x000bb394, 0x00000000 }, + { 0x0000aad4, 0x00000000, 0x00000000, 0x000bb798, 0x000bb798, 0x00000000 }, + { 0x0000aad8, 0x00000000, 0x00000000, 0x000f970c, 0x000f970c, 0x00000000 }, + { 0x0000aadc, 0x00000000, 0x00000000, 0x000f9710, 0x000f9710, 0x00000000 }, + { 0x0000aae0, 0x00000000, 0x00000000, 0x000f9714, 0x000f9714, 0x00000000 }, + { 0x0000aae4, 0x00000000, 0x00000000, 0x000f9718, 0x000f9718, 0x00000000 }, + { 0x0000aae8, 0x00000000, 0x00000000, 0x000f9705, 0x000f9705, 0x00000000 }, + { 0x0000aaec, 0x00000000, 0x00000000, 0x000f9709, 0x000f9709, 0x00000000 }, + { 0x0000aaf0, 0x00000000, 0x00000000, 0x000f970d, 0x000f970d, 0x00000000 }, + { 0x0000aaf4, 0x00000000, 0x00000000, 0x000f9711, 0x000f9711, 0x00000000 }, + { 0x0000aaf8, 0x00000000, 0x00000000, 0x000f9715, 0x000f9715, 0x00000000 }, + { 0x0000aafc, 0x00000000, 0x00000000, 0x000f9719, 0x000f9719, 0x00000000 }, + { 0x0000ab00, 0x00000000, 0x00000000, 0x000fb7a4, 0x000fb7a4, 0x00000000 }, + { 0x0000ab04, 0x00000000, 0x00000000, 0x000fb7a8, 0x000fb7a8, 0x00000000 }, + { 0x0000ab08, 0x00000000, 0x00000000, 0x000fb7ac, 0x000fb7ac, 0x00000000 }, + { 0x0000ab0c, 0x00000000, 0x00000000, 0x000fb7ac, 0x000fb7ac, 0x00000000 }, + { 0x0000ab10, 0x00000000, 0x00000000, 0x000fb7b0, 0x000fb7b0, 0x00000000 }, + { 0x0000ab14, 0x00000000, 0x00000000, 0x000fb7b8, 0x000fb7b8, 0x00000000 }, + { 0x0000ab18, 0x00000000, 0x00000000, 0x000fb7bc, 0x000fb7bc, 0x00000000 }, + { 0x0000ab1c, 0x00000000, 0x00000000, 0x000fb7a1, 0x000fb7a1, 0x00000000 }, + { 0x0000ab20, 0x00000000, 0x00000000, 0x000fb7a5, 0x000fb7a5, 0x00000000 }, + { 0x0000ab24, 0x00000000, 0x00000000, 0x000fb7a9, 0x000fb7a9, 0x00000000 }, + { 0x0000ab28, 0x00000000, 0x00000000, 0x000fb7b1, 0x000fb7b1, 0x00000000 }, + { 0x0000ab2c, 0x00000000, 0x00000000, 0x000fb7b5, 0x000fb7b5, 0x00000000 }, + { 0x0000ab30, 0x00000000, 0x00000000, 0x000fb7bd, 0x000fb7bd, 0x00000000 }, + { 0x0000ab34, 0x00000000, 0x00000000, 0x000fb7c9, 0x000fb7c9, 0x00000000 }, + { 0x0000ab38, 0x00000000, 0x00000000, 0x000fb7cd, 0x000fb7cd, 0x00000000 }, + { 0x0000ab3c, 0x00000000, 0x00000000, 0x000fb7d1, 0x000fb7d1, 0x00000000 }, + { 0x0000ab40, 0x00000000, 0x00000000, 0x000fb7d9, 0x000fb7d9, 0x00000000 }, + { 0x0000ab44, 0x00000000, 0x00000000, 0x000fb7c2, 0x000fb7c2, 0x00000000 }, + { 0x0000ab48, 0x00000000, 0x00000000, 0x000fb7c6, 0x000fb7c6, 0x00000000 }, + { 0x0000ab4c, 0x00000000, 0x00000000, 0x000fb7ca, 0x000fb7ca, 0x00000000 }, + { 0x0000ab50, 0x00000000, 0x00000000, 0x000fb7ce, 0x000fb7ce, 0x00000000 }, + { 0x0000ab54, 0x00000000, 0x00000000, 0x000fb7d2, 0x000fb7d2, 0x00000000 }, + { 0x0000ab58, 0x00000000, 0x00000000, 0x000fb7d6, 0x000fb7d6, 0x00000000 }, + { 0x0000ab5c, 0x00000000, 0x00000000, 0x000fb7c3, 0x000fb7c3, 0x00000000 }, + { 0x0000ab60, 0x00000000, 0x00000000, 0x000fb7cb, 0x000fb7cb, 0x00000000 }, + { 0x0000ab64, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab68, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab6c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab70, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab74, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab78, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab7c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab80, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab84, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab88, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab8c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab90, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab94, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab98, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000ab9c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000aba0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000aba4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000aba8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abac, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abb0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abb4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abb8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abbc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abc0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abc4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abc8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abcc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abd0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abd4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abd8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abdc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abe0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abe4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abe8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abec, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abf0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abf4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abf8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000abfc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 }, + { 0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004 }, + { 0x0000a20c, 0x00000014, 0x00000014, 0x00000000, 0x00000000, 0x0001f000 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a250, 0x001ff000, 0x001ff000, 0x001ca000, 0x001ca000, 0x001da000 }, + { 0x0000a274, 0x0a81c652, 0x0a81c652, 0x0a820652, 0x0a820652, 0x0a82a652 }, + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00007201, 0x00007201, 0x00000000 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00010408, 0x00010408, 0x00000000 }, + { 0x0000a30c, 0x00000000, 0x00000000, 0x0001860a, 0x0001860a, 0x00000000 }, + { 0x0000a310, 0x00000000, 0x00000000, 0x00020818, 0x00020818, 0x00000000 }, + { 0x0000a314, 0x00000000, 0x00000000, 0x00024858, 0x00024858, 0x00000000 }, + { 0x0000a318, 0x00000000, 0x00000000, 0x00026859, 0x00026859, 0x00000000 }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x0002985b, 0x0002985b, 0x00000000 }, + { 0x0000a320, 0x00000000, 0x00000000, 0x0002c89a, 0x0002c89a, 0x00000000 }, + { 0x0000a324, 0x00000000, 0x00000000, 0x0002e89b, 0x0002e89b, 0x00000000 }, + { 0x0000a328, 0x00000000, 0x00000000, 0x0003089c, 0x0003089c, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x0003289d, 0x0003289d, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x0003489e, 0x0003489e, 0x00000000 }, + { 0x0000a334, 0x00000000, 0x00000000, 0x000388de, 0x000388de, 0x00000000 }, + { 0x0000a338, 0x00000000, 0x00000000, 0x0003b91e, 0x0003b91e, 0x00000000 }, + { 0x0000a33c, 0x00000000, 0x00000000, 0x0003d95e, 0x0003d95e, 0x00000000 }, + { 0x0000a340, 0x00000000, 0x00000000, 0x000419df, 0x000419df, 0x00000000 }, + { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, +}; + +static const u_int32_t ar9285Common_9285[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020045 }, + { 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 }, + { 0x00004060, 0x00000000 }, + { 0x00004064, 0x00000000 }, + { 0x00007010, 0x00000031 }, + { 0x00007034, 0x00000002 }, + { 0x00007038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x00000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x00008070, 0x00000000 }, + { 0x000080c0, 0x2a80001a }, + { 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 }, + { 0x000081c0, 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 }, + { 0x00008264, 0xa8a00010 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x0000829c, 0x00000000 }, + { 0x00008300, 0x00000040 }, + { 0x00008314, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000001 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00000000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x00010380 }, + { 0x00008344, 0x00581043 }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xafe68e30 }, + { 0x00009810, 0xfd14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x0000984c, 0x0040233c }, + { 0x00009854, 0x00000044 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x00009910, 0x01002310 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x04900000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009940, 0x14750604 }, + { 0x00009948, 0x9280c00a }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5f3ca3de }, + { 0x00009958, 0x2108ecff }, + { 0x00009968, 0x000003ce }, + { 0x00009970, 0x1927b515 }, + { 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, 0x2def0a00 }, + { 0x000099b0, 0x03051000 }, + { 0x000099b4, 0x00000820 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000000 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099f0, 0x00000000 }, + { 0x0000a208, 0x803e6788 }, + { 0x0000a210, 0x4080a333 }, + { 0x0000a214, 0x00206c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x01834061 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x000003b5 }, + { 0x0000a22c, 0x00000000 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a244, 0x00000000 }, + { 0x0000a248, 0xfffffffc }, + { 0x0000a24c, 0x00000000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0ccb5380 }, + { 0x0000a25c, 0x15151501 }, + { 0x0000a260, 0xdfa90f01 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0ebae9e6 }, + { 0x0000d270, 0x0d820820 }, + { 0x0000a278, 0x39ce739c }, + { 0x0000a27c, 0x050e039c }, + { 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, 0x39ce739c }, + { 0x0000a398, 0x0000039c }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x39ce739c }, + { 0x0000a3e0, 0x0000039c }, + { 0x0000a3e4, 0x00000000 }, + { 0x0000a3e8, 0x18c43433 }, + { 0x0000a3ec, 0x00f70081 }, + { 0x00007800, 0x00140000 }, + { 0x00007804, 0x0e4548d8 }, + { 0x00007808, 0x54214514 }, + { 0x0000780c, 0x02025820 }, + { 0x00007810, 0x71c0d388 }, + { 0x00007814, 0x924934a8 }, + { 0x0000781c, 0x00000000 }, + { 0x00007820, 0x00000c04 }, + { 0x00007824, 0x00d86fff }, + { 0x00007828, 0x26d2491b }, + { 0x0000782c, 0x6e36d97b }, + { 0x00007830, 0xedb6d96c }, + { 0x00007834, 0x71400086 }, + { 0x00007838, 0xfac68800 }, + { 0x0000783c, 0x0001fffe }, + { 0x00007840, 0xffeb1a20 }, + { 0x00007844, 0x000c0db6 }, + { 0x00007848, 0x6db61b6f }, + { 0x0000784c, 0x6d9b66db }, + { 0x00007850, 0x6d8c6dba }, + { 0x00007854, 0x00040000 }, + { 0x00007858, 0xdb003012 }, + { 0x0000785c, 0x04924914 }, + { 0x00007860, 0x21084210 }, + { 0x00007864, 0xf7d7ffde }, + { 0x00007868, 0xc2034080 }, + { 0x0000786c, 0x48609eb4 }, + { 0x00007870, 0x10142c00 }, +}; + +static const u_int32_t ar9285PciePhy_clkreq_always_on_L1_9285[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0xc01dcffd }, + {0x00004040, 0x1aaabe41 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; + +static const u_int32_t ar9285PciePhy_clkreq_off_L1_9285[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0xc01dcffc }, + {0x00004040, 0x1aaabe41 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; + +/* AR9285 v1_2 PCI Register Writes. Created: 03/04/09 */ +static const u_int32_t ar9285Modes_9285_1_2[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f }, + { 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 }, + { 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 }, + { 0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e }, + { 0x00009844, 0x0372161e, 0x0372161e, 0x03721620, 0x03721620, 0x037216a0 }, + { 0x00009848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059 }, + { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 }, + { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e }, + { 0x00009860, 0x00058d18, 0x00058d18, 0x00058d20, 0x00058d20, 0x00058d18 }, + { 0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, + { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d }, + { 0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1020, 0xffbc1020, 0xffbc1010 }, + { 0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099b8, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c }, + { 0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329 }, + { 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, 0x00000000, 0x00000000, 0x00058084, 0x00058084, 0x00000000 }, + { 0x00009a04, 0x00000000, 0x00000000, 0x00058088, 0x00058088, 0x00000000 }, + { 0x00009a08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c, 0x00000000 }, + { 0x00009a0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100, 0x00000000 }, + { 0x00009a10, 0x00000000, 0x00000000, 0x00058104, 0x00058104, 0x00000000 }, + { 0x00009a14, 0x00000000, 0x00000000, 0x00058108, 0x00058108, 0x00000000 }, + { 0x00009a18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c, 0x00000000 }, + { 0x00009a1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110, 0x00000000 }, + { 0x00009a20, 0x00000000, 0x00000000, 0x00058114, 0x00058114, 0x00000000 }, + { 0x00009a24, 0x00000000, 0x00000000, 0x00058180, 0x00058180, 0x00000000 }, + { 0x00009a28, 0x00000000, 0x00000000, 0x00058184, 0x00058184, 0x00000000 }, + { 0x00009a2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188, 0x00000000 }, + { 0x00009a30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c, 0x00000000 }, + { 0x00009a34, 0x00000000, 0x00000000, 0x00058190, 0x00058190, 0x00000000 }, + { 0x00009a38, 0x00000000, 0x00000000, 0x00058194, 0x00058194, 0x00000000 }, + { 0x00009a3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0, 0x00000000 }, + { 0x00009a40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c, 0x00000000 }, + { 0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 }, + { 0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 }, + { 0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 }, + { 0x00009a50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 }, + { 0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 }, + { 0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 }, + { 0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 }, + { 0x00009a60, 0x00000000, 0x00000000, 0x00058308, 0x00058308, 0x00000000 }, + { 0x00009a64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c, 0x00000000 }, + { 0x00009a68, 0x00000000, 0x00000000, 0x00058380, 0x00058380, 0x00000000 }, + { 0x00009a6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384, 0x00000000 }, + { 0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000 }, + { 0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000 }, + { 0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000 }, + { 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 }, + { 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, + { 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, + { 0x00009a88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, + { 0x00009a8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 }, + { 0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 }, + { 0x00009a9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84, 0x00000000 }, + { 0x00009aa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88, 0x00000000 }, + { 0x00009aa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c, 0x00000000 }, + { 0x00009aa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90, 0x00000000 }, + { 0x00009aac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80, 0x00000000 }, + { 0x00009ab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84, 0x00000000 }, + { 0x00009ab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88, 0x00000000 }, + { 0x00009ab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c, 0x00000000 }, + { 0x00009abc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90, 0x00000000 }, + { 0x00009ac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c, 0x00000000 }, + { 0x00009ac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310, 0x00000000 }, + { 0x00009ac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384, 0x00000000 }, + { 0x00009acc, 0x00000000, 0x00000000, 0x000db388, 0x000db388, 0x00000000 }, + { 0x00009ad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324, 0x00000000 }, + { 0x00009ad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704, 0x00000000 }, + { 0x00009ad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4, 0x00000000 }, + { 0x00009adc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8, 0x00000000 }, + { 0x00009ae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710, 0x00000000 }, + { 0x00009ae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714, 0x00000000 }, + { 0x00009ae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720, 0x00000000 }, + { 0x00009aec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724, 0x00000000 }, + { 0x00009af0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728, 0x00000000 }, + { 0x00009af4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c, 0x00000000 }, + { 0x00009af8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0, 0x00000000 }, + { 0x00009afc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4, 0x00000000 }, + { 0x00009b00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8, 0x00000000 }, + { 0x00009b04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0, 0x00000000 }, + { 0x00009b08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4, 0x00000000 }, + { 0x00009b0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8, 0x00000000 }, + { 0x00009b10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5, 0x00000000 }, + { 0x00009b14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9, 0x00000000 }, + { 0x00009b18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad, 0x00000000 }, + { 0x00009b1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1, 0x00000000 }, + { 0x00009b20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5, 0x00000000 }, + { 0x00009b24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9, 0x00000000 }, + { 0x00009b28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5, 0x00000000 }, + { 0x00009b2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9, 0x00000000 }, + { 0x00009b30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1, 0x00000000 }, + { 0x00009b34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5, 0x00000000 }, + { 0x00009b38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9, 0x00000000 }, + { 0x00009b3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6, 0x00000000 }, + { 0x00009b40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca, 0x00000000 }, + { 0x00009b44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce, 0x00000000 }, + { 0x00009b48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2, 0x00000000 }, + { 0x00009b4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6, 0x00000000 }, + { 0x00009b50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3, 0x00000000 }, + { 0x00009b54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7, 0x00000000 }, + { 0x00009b58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb, 0x00000000 }, + { 0x00009b5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf, 0x00000000 }, + { 0x00009b60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7, 0x00000000 }, + { 0x00009b64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009ba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009ba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009ba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009be0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009be4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009be8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000aa00, 0x00000000, 0x00000000, 0x00058084, 0x00058084, 0x00000000 }, + { 0x0000aa04, 0x00000000, 0x00000000, 0x00058088, 0x00058088, 0x00000000 }, + { 0x0000aa08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c, 0x00000000 }, + { 0x0000aa0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100, 0x00000000 }, + { 0x0000aa10, 0x00000000, 0x00000000, 0x00058104, 0x00058104, 0x00000000 }, + { 0x0000aa14, 0x00000000, 0x00000000, 0x00058108, 0x00058108, 0x00000000 }, + { 0x0000aa18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c, 0x00000000 }, + { 0x0000aa1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110, 0x00000000 }, + { 0x0000aa20, 0x00000000, 0x00000000, 0x00058114, 0x00058114, 0x00000000 }, + { 0x0000aa24, 0x00000000, 0x00000000, 0x00058180, 0x00058180, 0x00000000 }, + { 0x0000aa28, 0x00000000, 0x00000000, 0x00058184, 0x00058184, 0x00000000 }, + { 0x0000aa2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188, 0x00000000 }, + { 0x0000aa30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c, 0x00000000 }, + { 0x0000aa34, 0x00000000, 0x00000000, 0x00058190, 0x00058190, 0x00000000 }, + { 0x0000aa38, 0x00000000, 0x00000000, 0x00058194, 0x00058194, 0x00000000 }, + { 0x0000aa3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0, 0x00000000 }, + { 0x0000aa40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c, 0x00000000 }, + { 0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 }, + { 0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 }, + { 0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 }, + { 0x0000aa50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 }, + { 0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 }, + { 0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 }, + { 0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 }, + { 0x0000aa60, 0x00000000, 0x00000000, 0x00058308, 0x00058308, 0x00000000 }, + { 0x0000aa64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c, 0x00000000 }, + { 0x0000aa68, 0x00000000, 0x00000000, 0x00058380, 0x00058380, 0x00000000 }, + { 0x0000aa6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384, 0x00000000 }, + { 0x0000aa70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000 }, + { 0x0000aa74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000 }, + { 0x0000aa78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000 }, + { 0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 }, + { 0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, + { 0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, + { 0x0000aa88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, + { 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 }, + { 0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 }, + { 0x0000aa9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84, 0x00000000 }, + { 0x0000aaa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88, 0x00000000 }, + { 0x0000aaa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c, 0x00000000 }, + { 0x0000aaa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90, 0x00000000 }, + { 0x0000aaac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80, 0x00000000 }, + { 0x0000aab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84, 0x00000000 }, + { 0x0000aab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88, 0x00000000 }, + { 0x0000aab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c, 0x00000000 }, + { 0x0000aabc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90, 0x00000000 }, + { 0x0000aac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c, 0x00000000 }, + { 0x0000aac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310, 0x00000000 }, + { 0x0000aac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384, 0x00000000 }, + { 0x0000aacc, 0x00000000, 0x00000000, 0x000db388, 0x000db388, 0x00000000 }, + { 0x0000aad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324, 0x00000000 }, + { 0x0000aad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704, 0x00000000 }, + { 0x0000aad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4, 0x00000000 }, + { 0x0000aadc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8, 0x00000000 }, + { 0x0000aae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710, 0x00000000 }, + { 0x0000aae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714, 0x00000000 }, + { 0x0000aae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720, 0x00000000 }, + { 0x0000aaec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724, 0x00000000 }, + { 0x0000aaf0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728, 0x00000000 }, + { 0x0000aaf4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c, 0x00000000 }, + { 0x0000aaf8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0, 0x00000000 }, + { 0x0000aafc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4, 0x00000000 }, + { 0x0000ab00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8, 0x00000000 }, + { 0x0000ab04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0, 0x00000000 }, + { 0x0000ab08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4, 0x00000000 }, + { 0x0000ab0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8, 0x00000000 }, + { 0x0000ab10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5, 0x00000000 }, + { 0x0000ab14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9, 0x00000000 }, + { 0x0000ab18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad, 0x00000000 }, + { 0x0000ab1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1, 0x00000000 }, + { 0x0000ab20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5, 0x00000000 }, + { 0x0000ab24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9, 0x00000000 }, + { 0x0000ab28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5, 0x00000000 }, + { 0x0000ab2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9, 0x00000000 }, + { 0x0000ab30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1, 0x00000000 }, + { 0x0000ab34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5, 0x00000000 }, + { 0x0000ab38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9, 0x00000000 }, + { 0x0000ab3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6, 0x00000000 }, + { 0x0000ab40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca, 0x00000000 }, + { 0x0000ab44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce, 0x00000000 }, + { 0x0000ab48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2, 0x00000000 }, + { 0x0000ab4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6, 0x00000000 }, + { 0x0000ab50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3, 0x00000000 }, + { 0x0000ab54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7, 0x00000000 }, + { 0x0000ab58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb, 0x00000000 }, + { 0x0000ab5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf, 0x00000000 }, + { 0x0000ab60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7, 0x00000000 }, + { 0x0000ab64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000aba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000aba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000aba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abe0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abe4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abe8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004 }, + { 0x0000a20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 }, + { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, +}; + +static const u_int32_t ar9285Common_9285_1_2[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020045 }, + { 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 }, + { 0x00004060, 0x00000000 }, + { 0x00004064, 0x00000000 }, + { 0x00007010, 0x00000031 }, + { 0x00007034, 0x00000002 }, + { 0x00007038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x00000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x00008070, 0x00000000 }, + { 0x000080c0, 0x2a80001a }, + { 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, 0x08f04810 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0xffffffff }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c0, 0x00000000 }, + { 0x000081d0, 0x0000320a }, + { 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 }, + { 0x00008264, 0xa8a00010 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x0000829c, 0x00000000 }, + { 0x00008300, 0x00000040 }, + { 0x00008314, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000001 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00ff0000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x00010380 }, + { 0x00008344, 0x00581043 }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xafe68e30 }, + { 0x00009810, 0xfd14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x0000984c, 0x0040233c }, + { 0x00009854, 0x00000044 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x00009910, 0x01002310 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x04900000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009940, 0x14750604 }, + { 0x00009948, 0x9280c00a }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5f3ca3de }, + { 0x00009958, 0x2108ecff }, + { 0x00009968, 0x000003ce }, + { 0x00009970, 0x192bb515 }, + { 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, 0x2def0400 }, + { 0x000099b0, 0x03051000 }, + { 0x000099b4, 0x00000820 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000000 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099f0, 0x00000000 }, + { 0x0000a208, 0x803e68c8 }, + { 0x0000a210, 0x4080a333 }, + { 0x0000a214, 0x00206c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x01834061 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x000003b5 }, + { 0x0000a22c, 0x00000000 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a244, 0x00000000 }, + { 0x0000a248, 0xfffffffc }, + { 0x0000a24c, 0x00000000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0ccb5380 }, + { 0x0000a25c, 0x15151501 }, + { 0x0000a260, 0xdfa90f01 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0ebae9e6 }, + { 0x0000d270, 0x0d820820 }, + { 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 }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3e4, 0x00000000 }, + { 0x0000a3e8, 0x18c43433 }, + { 0x0000a3ec, 0x00f70081 }, + { 0x00007800, 0x00140000 }, + { 0x00007804, 0x0e4548d8 }, + { 0x00007808, 0x54214514 }, + { 0x0000780c, 0x02025820 }, + { 0x00007810, 0x71c0d388 }, + { 0x00007814, 0x924934a8 }, + { 0x0000781c, 0x00000000 }, + { 0x00007824, 0x00d86fff }, + { 0x00007828, 0x26d2491b }, + { 0x0000782c, 0x6e36d97b }, + { 0x00007830, 0xedb6d96e }, + { 0x00007834, 0x71400087 }, + { 0x0000783c, 0x0001fffe }, + { 0x00007840, 0xffeb1a20 }, + { 0x00007844, 0x000c0db6 }, + { 0x00007848, 0x6db61b6f }, + { 0x0000784c, 0x6d9b66db }, + { 0x00007850, 0x6d8c6dba }, + { 0x00007854, 0x00040000 }, + { 0x00007858, 0xdb003012 }, + { 0x0000785c, 0x04924914 }, + { 0x00007860, 0x21084210 }, + { 0x00007864, 0xf7d7ffde }, + { 0x00007868, 0xc2034080 }, + { 0x00007870, 0x10142c00 }, +}; + +static const u_int32_t ar9285Modes_high_power_tx_gain_9285_1_2[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00005200, 0x00005200, 0x00000000 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00007201, 0x00007201, 0x00000000 }, + { 0x0000a30c, 0x00000000, 0x00000000, 0x0000b240, 0x0000b240, 0x00000000 }, + { 0x0000a310, 0x00000000, 0x00000000, 0x0000d241, 0x0000d241, 0x00000000 }, + { 0x0000a314, 0x00000000, 0x00000000, 0x0000f440, 0x0000f440, 0x00000000 }, + { 0x0000a318, 0x00000000, 0x00000000, 0x00014640, 0x00014640, 0x00000000 }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x00018680, 0x00018680, 0x00000000 }, + { 0x0000a320, 0x00000000, 0x00000000, 0x00019841, 0x00019841, 0x00000000 }, + { 0x0000a324, 0x00000000, 0x00000000, 0x0001ca40, 0x0001ca40, 0x00000000 }, + { 0x0000a328, 0x00000000, 0x00000000, 0x0001fa80, 0x0001fa80, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x00023ac0, 0x00023ac0, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40, 0x00000000 }, + { 0x0000a334, 0x00000000, 0x00000000, 0x00033d82, 0x00033d82, 0x00000000 }, + { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 }, + { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 }, + { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x00007838, 0xfac68803, 0xfac68803, 0xfac68803, 0xfac68803, 0xfac68803 }, + { 0x0000786c, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe }, + { 0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a21a652, 0x0a21a652, 0x0a22a652 }, + { 0x0000a278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce }, + { 0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce }, + { 0x0000a394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce }, + { 0x0000a398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce }, + { 0x0000a3dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce }, +}; + +static const u_int32_t ar9285Modes_original_tx_gain_9285_1_2[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000 }, + { 0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000 }, + { 0x0000a310, 0x00000000, 0x00000000, 0x00022618, 0x00022618, 0x00000000 }, + { 0x0000a314, 0x00000000, 0x00000000, 0x0002a6c9, 0x0002a6c9, 0x00000000 }, + { 0x0000a318, 0x00000000, 0x00000000, 0x00031710, 0x00031710, 0x00000000 }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x00035718, 0x00035718, 0x00000000 }, + { 0x0000a320, 0x00000000, 0x00000000, 0x00038758, 0x00038758, 0x00000000 }, + { 0x0000a324, 0x00000000, 0x00000000, 0x0003c75a, 0x0003c75a, 0x00000000 }, + { 0x0000a328, 0x00000000, 0x00000000, 0x0004075c, 0x0004075c, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x0004475e, 0x0004475e, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x0004679f, 0x0004679f, 0x00000000 }, + { 0x0000a334, 0x00000000, 0x00000000, 0x000487df, 0x000487df, 0x00000000 }, + { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 }, + { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 }, + { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x00007838, 0xfac68801, 0xfac68801, 0xfac68801, 0xfac68801, 0xfac68801 }, + { 0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4 }, + { 0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04 }, + { 0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a21a652, 0x0a21a652, 0x0a22a652 }, + { 0x0000a278, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c }, + { 0x0000a27c, 0x050e039c, 0x050e039c, 0x050e039c, 0x050e039c, 0x050e039c }, + { 0x0000a394, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c }, + { 0x0000a398, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c }, + { 0x0000a3dc, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c }, + { 0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c }, +}; + +static const u_int32_t ar9285PciePhy_clkreq_always_on_L1_9285_1_2[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0xc01dcffd }, + {0x00004040, 0x1aaabe41 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; + +static const u_int32_t ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0xc01dcffc }, + {0x00004040, 0x1aaabe41 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c new file mode 100644 index 00000000000..8ae4ec21667 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -0,0 +1,976 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah, + struct ath9k_tx_queue_info *qi) +{ + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", + ah->txok_interrupt_mask, ah->txerr_interrupt_mask, + ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask, + ah->txurn_interrupt_mask); + + REG_WRITE(ah, AR_IMR_S0, + SM(ah->txok_interrupt_mask, AR_IMR_S0_QCU_TXOK) + | SM(ah->txdesc_interrupt_mask, AR_IMR_S0_QCU_TXDESC)); + REG_WRITE(ah, AR_IMR_S1, + SM(ah->txerr_interrupt_mask, AR_IMR_S1_QCU_TXERR) + | SM(ah->txeol_interrupt_mask, AR_IMR_S1_QCU_TXEOL)); + REG_RMW_FIELD(ah, AR_IMR_S2, + AR_IMR_S2_QCU_TXURN, ah->txurn_interrupt_mask); +} + +u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q) +{ + return REG_READ(ah, AR_QTXDP(q)); +} + +bool ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp) +{ + REG_WRITE(ah, AR_QTXDP(q), txdp); + + return true; +} + +bool ath9k_hw_txstart(struct ath_hw *ah, u32 q) +{ + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Enable TXE on queue: %u\n", q); + + REG_WRITE(ah, AR_Q_TXE, 1 << q); + + return true; +} + +u32 ath9k_hw_numtxpending(struct ath_hw *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_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel) +{ + u32 txcfg, curLevel, newLevel; + enum ath9k_int omask; + + if (ah->tx_trig_level >= MAX_TX_FIFO_THRESHOLD) + return false; + + omask = ath9k_hw_set_interrupts(ah, ah->mask_reg & ~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->tx_trig_level = newLevel; + + return newLevel != curLevel; +} + +bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) +{ +#define ATH9K_TX_STOP_DMA_TIMEOUT 4000 /* usec */ +#define ATH9K_TIME_QUANTUM 100 /* usec */ + + struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath9k_tx_queue_info *qi; + u32 tsfLow, j, wait; + u32 wait_time = ATH9K_TX_STOP_DMA_TIMEOUT / ATH9K_TIME_QUANTUM; + + if (q >= pCap->total_queues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Stopping TX DMA, " + "invalid queue: %u\n", q); + return false; + } + + qi = &ah->txq[q]; + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Stopping TX DMA, " + "inactive queue: %u\n", q); + return false; + } + + REG_WRITE(ah, AR_Q_TXD, 1 << q); + + for (wait = wait_time; wait != 0; wait--) { + if (ath9k_hw_numtxpending(ah, q) == 0) + break; + udelay(ATH9K_TIME_QUANTUM); + } + + if (ath9k_hw_numtxpending(ah, q)) { + 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, + "TSF has moved while trying to set " + "quiet time TSF: 0x%08x\n", 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 = wait_time; + while (ath9k_hw_numtxpending(ah, q)) { + if ((--wait) == 0) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "Failed to stop TX DMA in 100 " + "msec after killing last frame\n"); + break; + } + udelay(ATH9K_TIME_QUANTUM); + } + + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); + } + + REG_WRITE(ah, AR_Q_TXD, 0); + return wait != 0; + +#undef ATH9K_TX_STOP_DMA_TIMEOUT +#undef ATH9K_TIME_QUANTUM +} + +bool ath9k_hw_filltxdesc(struct ath_hw *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_hw *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_hw *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; + ath9k_hw_updatetxtriglevel(ah, true); + } + 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 = 0; + + return 0; +} + +void ath9k_hw_set11n_txdesc(struct ath_hw *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); + + txPower += ah->txpower_indexoffset; + 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_hw *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; + + 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_hw *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_hw *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_hw *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_hw *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_hw *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_hw *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_gettxintrtxqs(struct ath_hw *ah, u32 *txqs) +{ + *txqs &= ah->intr_txqs; + ah->intr_txqs &= ~(*txqs); +} + +bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, + const struct ath9k_tx_queue_info *qinfo) +{ + u32 cw; + struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath9k_tx_queue_info *qi; + + if (q >= pCap->total_queues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set TXQ properties, " + "invalid queue: %u\n", q); + return false; + } + + qi = &ah->txq[q]; + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set TXQ properties, " + "inactive queue: %u\n", q); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set queue properties for: %u\n", q); + + 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_get_txq_props(struct ath_hw *ah, int q, + struct ath9k_tx_queue_info *qinfo) +{ + struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath9k_tx_queue_info *qi; + + if (q >= pCap->total_queues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Get TXQ properties, " + "invalid queue: %u\n", q); + return false; + } + + qi = &ah->txq[q]; + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Get TXQ properties, " + "inactive queue: %u\n", q); + 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; +} + +int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, + const struct ath9k_tx_queue_info *qinfo) +{ + struct ath9k_tx_queue_info *qi; + struct ath9k_hw_capabilities *pCap = &ah->caps; + int q; + + switch (type) { + case ATH9K_TX_QUEUE_BEACON: + q = pCap->total_queues - 1; + break; + case ATH9K_TX_QUEUE_CAB: + q = pCap->total_queues - 2; + break; + case ATH9K_TX_QUEUE_PSPOLL: + q = 1; + break; + case ATH9K_TX_QUEUE_UAPSD: + q = pCap->total_queues - 3; + break; + case ATH9K_TX_QUEUE_DATA: + for (q = 0; q < pCap->total_queues; q++) + if (ah->txq[q].tqi_type == + ATH9K_TX_QUEUE_INACTIVE) + break; + if (q == pCap->total_queues) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "No available TX queue\n"); + return -1; + } + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Invalid TX queue type: %u\n", + type); + return -1; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Setup TX queue: %u\n", q); + + qi = &ah->txq[q]; + if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "TX queue: %u already active\n", 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_physCompBuf; + (void) ath9k_hw_set_txq_props(ah, q, qinfo); + } + + return q; +} + +bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q) +{ + struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath9k_tx_queue_info *qi; + + if (q >= pCap->total_queues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TXQ, " + "invalid queue: %u\n", q); + return false; + } + qi = &ah->txq[q]; + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TXQ, " + "inactive queue: %u\n", q); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TX queue: %u\n", q); + + qi->tqi_type = ATH9K_TX_QUEUE_INACTIVE; + ah->txok_interrupt_mask &= ~(1 << q); + ah->txerr_interrupt_mask &= ~(1 << q); + ah->txdesc_interrupt_mask &= ~(1 << q); + ah->txeol_interrupt_mask &= ~(1 << q); + ah->txurn_interrupt_mask &= ~(1 << q); + ath9k_hw_set_txq_interrupts(ah, qi); + + return true; +} + +bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) +{ + struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath9k_channel *chan = ah->curchan; + struct ath9k_tx_queue_info *qi; + u32 cwMin, chanCwMin, value; + + if (q >= pCap->total_queues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TXQ, " + "invalid queue: %u\n", q); + return false; + } + + qi = &ah->txq[q]; + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TXQ, " + "inactive queue: %u\n", q); + return true; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TX queue: %u\n", 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->config.sw_beacon_response_time - + ah->config.dma_beacon_response_time) - + ah->config.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) + ah->txok_interrupt_mask |= 1 << q; + else + ah->txok_interrupt_mask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXERRINT_ENABLE) + ah->txerr_interrupt_mask |= 1 << q; + else + ah->txerr_interrupt_mask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXDESCINT_ENABLE) + ah->txdesc_interrupt_mask |= 1 << q; + else + ah->txdesc_interrupt_mask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXEOLINT_ENABLE) + ah->txeol_interrupt_mask |= 1 << q; + else + ah->txeol_interrupt_mask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXURNINT_ENABLE) + ah->txurn_interrupt_mask |= 1 << q; + else + ah->txurn_interrupt_mask &= ~(1 << q); + ath9k_hw_set_txq_interrupts(ah, qi); + + return true; +} + +int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, + u32 pa, struct ath_desc *nds, u64 tsf) +{ + struct ar5416_desc ads; + struct ar5416_desc *adsp = AR5416DESC(ds); + u32 phyerr; + + 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) { + 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; +} + +bool ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, + u32 size, u32 flags) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + struct ath9k_hw_capabilities *pCap = &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->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) + memset(&(ads->u), 0, sizeof(ads->u)); + + return true; +} + +bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set) +{ + u32 reg; + + 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, AH_WAIT_TIMEOUT)) { + 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, + "RX failed to go idle in 10 ms RXSM=0x%x\n", reg); + + return false; + } + } else { + REG_CLR_BIT(ah, AR_DIAG_SW, + (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + } + + return true; +} + +void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp) +{ + REG_WRITE(ah, AR_RXDP, rxdp); +} + +void ath9k_hw_rxena(struct ath_hw *ah) +{ + REG_WRITE(ah, AR_CR, AR_CR_RXE); +} + +void ath9k_hw_startpcureceive(struct ath_hw *ah) +{ + ath9k_enable_mib_counters(ah); + + ath9k_ani_reset(ah); + + REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); +} + +void ath9k_hw_stoppcurecv(struct ath_hw *ah) +{ + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + + ath9k_hw_disable_mib_counters(ah); +} + +bool ath9k_hw_stopdmarecv(struct ath_hw *ah) +{ +#define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ +#define AH_RX_TIME_QUANTUM 100 /* usec */ + + int i; + + REG_WRITE(ah, AR_CR, AR_CR_RXD); + + /* Wait for rx enable bit to go low */ + for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) { + if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0) + break; + udelay(AH_TIME_QUANTUM); + } + + if (i == 0) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "DMA failed to stop in %d ms " + "AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", + AH_RX_STOP_DMA_TIMEOUT / 1000, + REG_READ(ah, AR_CR), + REG_READ(ah, AR_DIAG_SW)); + return false; + } else { + return true; + } + +#undef AH_RX_TIME_QUANTUM +#undef AH_RX_STOP_DMA_TIMEOUT +} diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h new file mode 100644 index 00000000000..1176bce8b76 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -0,0 +1,680 @@ +/* + * Copyright (c) 2008-2009 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 MAC_H +#define MAC_H + +#define RXSTATUS_RATE(ah, ads) (AR_SREV_5416_20_OR_LATER(ah) ? \ + MS(ads->ds_rxstatus0, AR_RxRate) : \ + (ads->ds_rxstatus3 >> 2) & 0xFF) + +#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 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 + +#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 ATH9K_SLOT_TIME_6 6 +#define ATH9K_SLOT_TIME_9 9 +#define ATH9K_SLOT_TIME_20 20 + +#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 MIN_TX_FIFO_THRESHOLD 0x1 +#define MAX_TX_FIFO_THRESHOLD ((4096 / 64) - 1) +#define INIT_TX_FIFO_THRESHOLD MIN_TX_FIFO_THRESHOLD + +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 +/* ATH9K_TXDESC_INTREQ forces a tx interrupt to be generated for + * the descriptor its marked on. We take a tx interrupt to reap + * descriptors when the h/w hits an EOL condition or + * when the descriptor is specifically marked to generate + * an interrupt with this flag. Descriptors should be + * marked periodically to insure timely replenishing of the + * supply needed for sending frames. Defering interrupts + * reduces system load and potentially allows more concurrent + * work to be done but if done to aggressively can cause + * senders to backup. When the hardware queue is left too + * large rate control information may also be too out of + * date. An Alternative for this is TX interrupt mitigation + * but this needs more testing. */ +#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_TXDESC_CAB 0x0400 + +#define ATH9K_RXDESC_INTREQ 0x0020 + +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 + +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, +}; + +#define ATH9K_TXQ_USEDEFAULT ((u32) -1) +#define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001 + +#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_PHYERR = 0x00000100, + ATH9K_RX_FILTER_MYBEACON = 0x00000200, + ATH9K_RX_FILTER_PSPOLL = 0x00004000, + ATH9K_RX_FILTER_PHYRADAR = 0x00002000, + ATH9K_RX_FILTER_MCAST_BCAST_ALL = 0x00008000, +}; + +#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; +}; + +struct ath9k_keyval { + u8 kv_type; + u8 kv_pad; + u16 kv_len; + u8 kv_val[16]; /* TK */ + u8 kv_mic[8]; /* Michael MIC key */ + u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware + * supports both MIC keys in the same key cache entry; + * in that case, kv_mic is the RX key) */ +}; + +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 +}; + +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 ath_hw; +struct ath9k_channel; +struct ath_rate_table; + +u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q); +bool ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp); +bool ath9k_hw_txstart(struct ath_hw *ah, u32 q); +u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q); +bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel); +bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q); +bool ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds, + u32 segLen, bool firstSeg, + bool lastSeg, const struct ath_desc *ds0); +void ath9k_hw_cleartxdesc(struct ath_hw *ah, struct ath_desc *ds); +int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds); +void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds, + u32 pktLen, enum ath9k_pkt_type type, u32 txPower, + u32 keyIx, enum ath9k_key_type keyType, u32 flags); +void ath9k_hw_set11n_ratescenario(struct ath_hw *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_aggr_first(struct ath_hw *ah, struct ath_desc *ds, + u32 aggrLen); +void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, struct ath_desc *ds, + u32 numDelims); +void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, struct ath_desc *ds); +void ath9k_hw_clr11n_aggr(struct ath_hw *ah, struct ath_desc *ds); +void ath9k_hw_set11n_burstduration(struct ath_hw *ah, struct ath_desc *ds, + u32 burstDuration); +void ath9k_hw_set11n_virtualmorefrag(struct ath_hw *ah, struct ath_desc *ds, + u32 vmf); +void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs); +bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, + const struct ath9k_tx_queue_info *qinfo); +bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q, + struct ath9k_tx_queue_info *qinfo); +int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, + const struct ath9k_tx_queue_info *qinfo); +bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q); +bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q); +int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, + u32 pa, struct ath_desc *nds, u64 tsf); +bool ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, + u32 size, u32 flags); +bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set); +void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp); +void ath9k_hw_rxena(struct ath_hw *ah); +void ath9k_hw_startpcureceive(struct ath_hw *ah); +void ath9k_hw_stoppcurecv(struct ath_hw *ah); +bool ath9k_hw_stopdmarecv(struct ath_hw *ah); + +#endif /* MAC_H */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c new file mode 100644 index 00000000000..f7baa406918 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -0,0 +1,2885 @@ +/* + * Copyright (c) 2008-2009 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 <linux/nl80211.h> +#include "ath9k.h" + +#define ATH_PCI_VERSION "0.1" + +static char *dev_info = "ath9k"; + +MODULE_AUTHOR("Atheros Communications"); +MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); +MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); + +static int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); + +/* We use the hw_value as an index into our private channel structure */ + +#define CHAN2G(_freq, _idx) { \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 20, \ +} + +#define CHAN5G(_freq, _idx) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 20, \ +} + +/* Some 2 GHz radios are actually tunable on 2312-2732 + * on 5 MHz steps, we support the channels which we know + * we have calibration data for all cards though to make + * this static */ +static struct ieee80211_channel ath9k_2ghz_chantable[] = { + CHAN2G(2412, 0), /* Channel 1 */ + CHAN2G(2417, 1), /* Channel 2 */ + CHAN2G(2422, 2), /* Channel 3 */ + CHAN2G(2427, 3), /* Channel 4 */ + CHAN2G(2432, 4), /* Channel 5 */ + CHAN2G(2437, 5), /* Channel 6 */ + CHAN2G(2442, 6), /* Channel 7 */ + CHAN2G(2447, 7), /* Channel 8 */ + CHAN2G(2452, 8), /* Channel 9 */ + CHAN2G(2457, 9), /* Channel 10 */ + CHAN2G(2462, 10), /* Channel 11 */ + CHAN2G(2467, 11), /* Channel 12 */ + CHAN2G(2472, 12), /* Channel 13 */ + CHAN2G(2484, 13), /* Channel 14 */ +}; + +/* Some 5 GHz radios are actually tunable on XXXX-YYYY + * on 5 MHz steps, we support the channels which we know + * we have calibration data for all cards though to make + * this static */ +static struct ieee80211_channel ath9k_5ghz_chantable[] = { + /* _We_ call this UNII 1 */ + CHAN5G(5180, 14), /* Channel 36 */ + CHAN5G(5200, 15), /* Channel 40 */ + CHAN5G(5220, 16), /* Channel 44 */ + CHAN5G(5240, 17), /* Channel 48 */ + /* _We_ call this UNII 2 */ + CHAN5G(5260, 18), /* Channel 52 */ + CHAN5G(5280, 19), /* Channel 56 */ + CHAN5G(5300, 20), /* Channel 60 */ + CHAN5G(5320, 21), /* Channel 64 */ + /* _We_ call this "Middle band" */ + CHAN5G(5500, 22), /* Channel 100 */ + CHAN5G(5520, 23), /* Channel 104 */ + CHAN5G(5540, 24), /* Channel 108 */ + CHAN5G(5560, 25), /* Channel 112 */ + CHAN5G(5580, 26), /* Channel 116 */ + CHAN5G(5600, 27), /* Channel 120 */ + CHAN5G(5620, 28), /* Channel 124 */ + CHAN5G(5640, 29), /* Channel 128 */ + CHAN5G(5660, 30), /* Channel 132 */ + CHAN5G(5680, 31), /* Channel 136 */ + CHAN5G(5700, 32), /* Channel 140 */ + /* _We_ call this UNII 3 */ + CHAN5G(5745, 33), /* Channel 149 */ + CHAN5G(5765, 34), /* Channel 153 */ + CHAN5G(5785, 35), /* Channel 157 */ + CHAN5G(5805, 36), /* Channel 161 */ + CHAN5G(5825, 37), /* Channel 165 */ +}; + +static void ath_cache_conf_rate(struct ath_softc *sc, + struct ieee80211_conf *conf) +{ + switch (conf->channel->band) { + case IEEE80211_BAND_2GHZ: + if (conf_is_ht20(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NG_HT20]; + else if (conf_is_ht40_minus(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS]; + else if (conf_is_ht40_plus(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS]; + else + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11G]; + break; + case IEEE80211_BAND_5GHZ: + if (conf_is_ht20(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NA_HT20]; + else if (conf_is_ht40_minus(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS]; + else if (conf_is_ht40_plus(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS]; + else + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11A]; + break; + default: + BUG_ON(1); + break; + } +} + +static void ath_update_txpow(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + u32 txpow; + + if (sc->curtxpow != sc->config.txpowlimit) { + ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit); + /* read back in case value is clamped */ + ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow); + sc->curtxpow = txpow; + } +} + +static u8 parse_mpdudensity(u8 mpdudensity) +{ + /* + * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": + * 0 for no restriction + * 1 for 1/4 us + * 2 for 1/2 us + * 3 for 1 us + * 4 for 2 us + * 5 for 4 us + * 6 for 8 us + * 7 for 16 us + */ + switch (mpdudensity) { + case 0: + return 0; + case 1: + case 2: + case 3: + /* Our lower layer calculations limit our precision to + 1 microsecond */ + return 1; + case 4: + return 2; + case 5: + return 4; + case 6: + return 8; + case 7: + return 16; + default: + return 0; + } +} + +static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band) +{ + const struct ath_rate_table *rate_table = NULL; + struct ieee80211_supported_band *sband; + struct ieee80211_rate *rate; + int i, maxrates; + + switch (band) { + case IEEE80211_BAND_2GHZ: + rate_table = sc->hw_rate_table[ATH9K_MODE_11G]; + break; + case IEEE80211_BAND_5GHZ: + rate_table = sc->hw_rate_table[ATH9K_MODE_11A]; + break; + default: + break; + } + + if (rate_table == NULL) + return; + + sband = &sc->sbands[band]; + rate = sc->rates[band]; + + if (rate_table->rate_cnt > ATH_RATE_MAX) + maxrates = ATH_RATE_MAX; + else + maxrates = rate_table->rate_cnt; + + for (i = 0; i < maxrates; i++) { + rate[i].bitrate = rate_table->info[i].ratekbps / 100; + rate[i].hw_value = rate_table->info[i].ratecode; + if (rate_table->info[i].short_preamble) { + rate[i].hw_value_short = rate_table->info[i].ratecode | + rate_table->info[i].short_preamble; + rate[i].flags = IEEE80211_RATE_SHORT_PREAMBLE; + } + sband->n_bitrates++; + + DPRINTF(sc, ATH_DBG_CONFIG, "Rate: %2dMbps, ratecode: %2d\n", + rate[i].bitrate / 10, rate[i].hw_value); + } +} + +/* + * 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. +*/ +int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, + struct ath9k_channel *hchan) +{ + struct ath_hw *ah = sc->sc_ah; + bool fastcc = true, stopped; + struct ieee80211_channel *channel = hw->conf.channel; + int r; + + if (sc->sc_flags & SC_OP_INVALID) + return -EIO; + + ath9k_ps_wakeup(sc); + + /* + * 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); + ath_drain_all_txq(sc, false); + stopped = ath_stoprecv(sc); + + /* 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_flags & SC_OP_FULL_RESET)) + fastcc = false; + + DPRINTF(sc, ATH_DBG_CONFIG, + "(%u MHz) -> (%u MHz), chanwidth: %d\n", + sc->sc_ah->curchan->channel, + channel->center_freq, sc->tx_chan_width); + + spin_lock_bh(&sc->sc_resetlock); + + r = ath9k_hw_reset(ah, hchan, fastcc); + if (r) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to reset channel (%u Mhz) " + "reset status %d\n", + channel->center_freq, r); + spin_unlock_bh(&sc->sc_resetlock); + return r; + } + spin_unlock_bh(&sc->sc_resetlock); + + sc->sc_flags &= ~SC_OP_FULL_RESET; + + if (ath_startrecv(sc) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to restart recv logic\n"); + return -EIO; + } + + ath_cache_conf_rate(sc, &hw->conf); + ath_update_txpow(sc); + ath9k_hw_set_interrupts(ah, sc->imask); + ath9k_ps_restore(sc); + return 0; +} + +/* + * This routine performs the periodic noise floor calibration function + * that is used to adjust and optimize the chip performance. This + * takes environmental changes (location, temperature) into account. + * When the task is complete, it reschedules itself depending on the + * appropriate interval that was calculated. + */ +static void ath_ani_calibrate(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + struct ath_hw *ah = sc->sc_ah; + bool longcal = false; + bool shortcal = false; + bool aniflag = false; + unsigned int timestamp = jiffies_to_msecs(jiffies); + u32 cal_interval, short_cal_interval; + + short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? + ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; + + /* + * don't calibrate when we're scanning. + * we are most likely not on our home channel. + */ + if (sc->sc_flags & SC_OP_SCANNING) + goto set_timer; + + /* Only calibrate if awake */ + if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) + goto set_timer; + + ath9k_ps_wakeup(sc); + + /* Long calibration runs independently of short calibration. */ + if ((timestamp - sc->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { + longcal = true; + DPRINTF(sc, ATH_DBG_ANI, "longcal @%lu\n", jiffies); + sc->ani.longcal_timer = timestamp; + } + + /* Short calibration applies only while caldone is false */ + if (!sc->ani.caldone) { + if ((timestamp - sc->ani.shortcal_timer) >= short_cal_interval) { + shortcal = true; + DPRINTF(sc, ATH_DBG_ANI, "shortcal @%lu\n", jiffies); + sc->ani.shortcal_timer = timestamp; + sc->ani.resetcal_timer = timestamp; + } + } else { + if ((timestamp - sc->ani.resetcal_timer) >= + ATH_RESTART_CALINTERVAL) { + sc->ani.caldone = ath9k_hw_reset_calvalid(ah); + if (sc->ani.caldone) + sc->ani.resetcal_timer = timestamp; + } + } + + /* Verify whether we must check ANI */ + if ((timestamp - sc->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { + aniflag = true; + sc->ani.checkani_timer = timestamp; + } + + /* Skip all processing if there's nothing to do. */ + if (longcal || shortcal || aniflag) { + /* Call ANI routine if necessary */ + if (aniflag) + ath9k_hw_ani_monitor(ah, &sc->nodestats, ah->curchan); + + /* Perform calibration if necessary */ + if (longcal || shortcal) { + sc->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan, + sc->rx_chainmask, longcal); + + if (longcal) + sc->ani.noise_floor = ath9k_hw_getchan_noise(ah, + ah->curchan); + + DPRINTF(sc, ATH_DBG_ANI," calibrate chan %u/%x nf: %d\n", + ah->curchan->channel, ah->curchan->channelFlags, + sc->ani.noise_floor); + } + } + + ath9k_ps_restore(sc); + +set_timer: + /* + * Set timer interval based on previous results. + * The interval must be the shortest necessary to satisfy ANI, + * short calibration and long calibration. + */ + cal_interval = ATH_LONG_CALINTERVAL; + if (sc->sc_ah->config.enable_ani) + cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); + if (!sc->ani.caldone) + cal_interval = min(cal_interval, (u32)short_cal_interval); + + mod_timer(&sc->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); +} + +static void ath_start_ani(struct ath_softc *sc) +{ + unsigned long timestamp = jiffies_to_msecs(jiffies); + + sc->ani.longcal_timer = timestamp; + sc->ani.shortcal_timer = timestamp; + sc->ani.checkani_timer = timestamp; + + mod_timer(&sc->ani.timer, + jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); +} + +/* + * Update tx/rx chainmask. For legacy association, + * hard code chainmask to 1x1, for 11n association, use + * the chainmask configuration, for bt coexistence, use + * the chainmask configuration even in legacy mode. + */ +void ath_update_chainmask(struct ath_softc *sc, int is_ht) +{ + if (is_ht || + (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BT_COEX)) { + sc->tx_chainmask = sc->sc_ah->caps.tx_chainmask; + sc->rx_chainmask = sc->sc_ah->caps.rx_chainmask; + } else { + sc->tx_chainmask = 1; + sc->rx_chainmask = 1; + } + + DPRINTF(sc, ATH_DBG_CONFIG, "tx chmask: %d, rx chmask: %d\n", + sc->tx_chainmask, sc->rx_chainmask); +} + +static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta) +{ + struct ath_node *an; + + an = (struct ath_node *)sta->drv_priv; + + if (sc->sc_flags & SC_OP_TXAGGR) { + ath_tx_node_init(sc, an); + an->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR + + sta->ht_cap.ampdu_factor); + an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density); + } +} + +static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) +{ + struct ath_node *an = (struct ath_node *)sta->drv_priv; + + if (sc->sc_flags & SC_OP_TXAGGR) + ath_tx_node_cleanup(sc, an); +} + +static void ath9k_tasklet(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + u32 status = sc->intrstatus; + + ath9k_ps_wakeup(sc); + + if (status & ATH9K_INT_FATAL) { + ath_reset(sc, false); + ath9k_ps_restore(sc); + return; + } + + if (status & (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN)) { + spin_lock_bh(&sc->rx.rxflushlock); + ath_rx_tasklet(sc, 0); + spin_unlock_bh(&sc->rx.rxflushlock); + } + + if (status & ATH9K_INT_TX) + ath_tx_tasklet(sc); + + if ((status & ATH9K_INT_TSFOOR) && + (sc->hw->conf.flags & IEEE80211_CONF_PS)) { + /* + * TSF sync does not look correct; remain awake to sync with + * the next Beacon. + */ + DPRINTF(sc, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n"); + sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC; + } + + /* re-enable hardware interrupt */ + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); + ath9k_ps_restore(sc); +} + +irqreturn_t ath_isr(int irq, void *dev) +{ +#define SCHED_INTR ( \ + ATH9K_INT_FATAL | \ + ATH9K_INT_RXORN | \ + ATH9K_INT_RXEOL | \ + ATH9K_INT_RX | \ + ATH9K_INT_TX | \ + ATH9K_INT_BMISS | \ + ATH9K_INT_CST | \ + ATH9K_INT_TSFOOR) + + struct ath_softc *sc = dev; + struct ath_hw *ah = sc->sc_ah; + enum ath9k_int status; + bool sched = false; + + /* + * The hardware is not ready/present, don't + * touch anything. Note this can happen early + * on if the IRQ is shared. + */ + if (sc->sc_flags & SC_OP_INVALID) + return IRQ_NONE; + + + /* shared irq, not for us */ + + if (!ath9k_hw_intrpend(ah)) + 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->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; + + /* Cache the status */ + sc->intrstatus = status; + + if (status & SCHED_INTR) + sched = true; + + /* + * If a FATAL or RXORN interrupt is received, we have to reset the + * chip immediately. + */ + if (status & (ATH9K_INT_FATAL | ATH9K_INT_RXORN)) + goto chip_reset; + + if (status & ATH9K_INT_SWBA) + tasklet_schedule(&sc->bcon_tasklet); + + if (status & ATH9K_INT_TXURN) + ath9k_hw_updatetxtriglevel(ah, 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->nodestats); + ath9k_hw_set_interrupts(ah, sc->imask); + } + + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) + if (status & ATH9K_INT_TIM_TIMER) { + /* Clear RxAbort bit so that we can + * receive frames */ + ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); + ath9k_hw_setrxabort(sc->sc_ah, 0); + sc->sc_flags |= SC_OP_WAIT_FOR_BEACON; + } + +chip_reset: + + ath_debug_stat_interrupt(sc, status); + + if (sched) { + /* turn off every interrupt except SWBA */ + ath9k_hw_set_interrupts(ah, (sc->imask & ATH9K_INT_SWBA)); + tasklet_schedule(&sc->intr_tq); + } + + return IRQ_HANDLED; + +#undef SCHED_INTR +} + +static u32 ath_get_extchanmode(struct ath_softc *sc, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + u32 chanmode = 0; + + switch (chan->band) { + case IEEE80211_BAND_2GHZ: + switch(channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + chanmode = CHANNEL_G_HT20; + break; + case NL80211_CHAN_HT40PLUS: + chanmode = CHANNEL_G_HT40PLUS; + break; + case NL80211_CHAN_HT40MINUS: + chanmode = CHANNEL_G_HT40MINUS; + break; + } + break; + case IEEE80211_BAND_5GHZ: + switch(channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + chanmode = CHANNEL_A_HT20; + break; + case NL80211_CHAN_HT40PLUS: + chanmode = CHANNEL_A_HT40PLUS; + break; + case NL80211_CHAN_HT40MINUS: + chanmode = CHANNEL_A_HT40MINUS; + break; + } + break; + default: + break; + } + + return chanmode; +} + +static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key, + struct ath9k_keyval *hk, const u8 *addr, + bool authenticator) +{ + const u8 *key_rxmic; + const u8 *key_txmic; + + key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; + key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; + + if (addr == NULL) { + /* + * Group key installation - only two key cache entries are used + * regardless of splitmic capability since group key is only + * used either for TX or RX. + */ + if (authenticator) { + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic)); + } else { + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic)); + } + return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, addr); + } + if (!sc->splitmic) { + /* TX and RX keys share the same key cache entry. */ + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); + return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, addr); + } + + /* Separate key cache entries for TX and RX */ + + /* TX key goes at first index, RX key at +32. */ + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + if (!ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, NULL)) { + /* TX MIC entry failed. No need to proceed further */ + DPRINTF(sc, ATH_DBG_FATAL, + "Setting TX MIC Key Failed\n"); + return 0; + } + + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + /* XXX delete tx key on failure? */ + return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix + 32, hk, addr); +} + +static int ath_reserve_key_cache_slot_tkip(struct ath_softc *sc) +{ + int i; + + for (i = IEEE80211_WEP_NKID; i < sc->keymax / 2; i++) { + if (test_bit(i, sc->keymap) || + test_bit(i + 64, sc->keymap)) + continue; /* At least one part of TKIP key allocated */ + if (sc->splitmic && + (test_bit(i + 32, sc->keymap) || + test_bit(i + 64 + 32, sc->keymap))) + continue; /* At least one part of TKIP key allocated */ + + /* Found a free slot for a TKIP key */ + return i; + } + return -1; +} + +static int ath_reserve_key_cache_slot(struct ath_softc *sc) +{ + int i; + + /* First, try to find slots that would not be available for TKIP. */ + if (sc->splitmic) { + for (i = IEEE80211_WEP_NKID; i < sc->keymax / 4; i++) { + if (!test_bit(i, sc->keymap) && + (test_bit(i + 32, sc->keymap) || + test_bit(i + 64, sc->keymap) || + test_bit(i + 64 + 32, sc->keymap))) + return i; + if (!test_bit(i + 32, sc->keymap) && + (test_bit(i, sc->keymap) || + test_bit(i + 64, sc->keymap) || + test_bit(i + 64 + 32, sc->keymap))) + return i + 32; + if (!test_bit(i + 64, sc->keymap) && + (test_bit(i , sc->keymap) || + test_bit(i + 32, sc->keymap) || + test_bit(i + 64 + 32, sc->keymap))) + return i + 64; + if (!test_bit(i + 64 + 32, sc->keymap) && + (test_bit(i, sc->keymap) || + test_bit(i + 32, sc->keymap) || + test_bit(i + 64, sc->keymap))) + return i + 64 + 32; + } + } else { + for (i = IEEE80211_WEP_NKID; i < sc->keymax / 2; i++) { + if (!test_bit(i, sc->keymap) && + test_bit(i + 64, sc->keymap)) + return i; + if (test_bit(i, sc->keymap) && + !test_bit(i + 64, sc->keymap)) + return i + 64; + } + } + + /* No partially used TKIP slots, pick any available slot */ + for (i = IEEE80211_WEP_NKID; i < sc->keymax; i++) { + /* Do not allow slots that could be needed for TKIP group keys + * to be used. This limitation could be removed if we know that + * TKIP will not be used. */ + if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) + continue; + if (sc->splitmic) { + if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) + continue; + if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) + continue; + } + + if (!test_bit(i, sc->keymap)) + return i; /* Found a free slot for a key */ + } + + /* No free slot found */ + return -1; +} + +static int ath_key_config(struct ath_softc *sc, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath9k_keyval hk; + const u8 *mac = NULL; + int ret = 0; + int idx; + + memset(&hk, 0, sizeof(hk)); + + switch (key->alg) { + case ALG_WEP: + hk.kv_type = ATH9K_CIPHER_WEP; + break; + case ALG_TKIP: + hk.kv_type = ATH9K_CIPHER_TKIP; + break; + case ALG_CCMP: + hk.kv_type = ATH9K_CIPHER_AES_CCM; + break; + default: + return -EOPNOTSUPP; + } + + hk.kv_len = key->keylen; + memcpy(hk.kv_val, key->key, key->keylen); + + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + /* For now, use the default keys for broadcast keys. This may + * need to change with virtual interfaces. */ + idx = key->keyidx; + } else if (key->keyidx) { + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + + if (vif->type != NL80211_IFTYPE_AP) { + /* Only keyidx 0 should be used with unicast key, but + * allow this for client mode for now. */ + idx = key->keyidx; + } else + return -EIO; + } else { + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + + if (key->alg == ALG_TKIP) + idx = ath_reserve_key_cache_slot_tkip(sc); + else + idx = ath_reserve_key_cache_slot(sc); + if (idx < 0) + return -ENOSPC; /* no free key cache entries */ + } + + if (key->alg == ALG_TKIP) + ret = ath_setkey_tkip(sc, idx, key->key, &hk, mac, + vif->type == NL80211_IFTYPE_AP); + else + ret = ath9k_hw_set_keycache_entry(sc->sc_ah, idx, &hk, mac); + + if (!ret) + return -EIO; + + set_bit(idx, sc->keymap); + if (key->alg == ALG_TKIP) { + set_bit(idx + 64, sc->keymap); + if (sc->splitmic) { + set_bit(idx + 32, sc->keymap); + set_bit(idx + 64 + 32, sc->keymap); + } + } + + return idx; +} + +static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key) +{ + ath9k_hw_keyreset(sc->sc_ah, key->hw_key_idx); + if (key->hw_key_idx < IEEE80211_WEP_NKID) + return; + + clear_bit(key->hw_key_idx, sc->keymap); + if (key->alg != ALG_TKIP) + return; + + clear_bit(key->hw_key_idx + 64, sc->keymap); + if (sc->splitmic) { + clear_bit(key->hw_key_idx + 32, sc->keymap); + clear_bit(key->hw_key_idx + 64 + 32, sc->keymap); + } +} + +static void setup_ht_cap(struct ath_softc *sc, + struct ieee80211_sta_ht_cap *ht_info) +{ +#define ATH9K_HT_CAP_MAXRXAMPDU_65536 0x3 /* 2 ^ 16 */ +#define ATH9K_HT_CAP_MPDUDENSITY_8 0x6 /* 8 usec */ + + ht_info->ht_supported = true; + ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40; + + ht_info->ampdu_factor = ATH9K_HT_CAP_MAXRXAMPDU_65536; + ht_info->ampdu_density = ATH9K_HT_CAP_MPDUDENSITY_8; + + /* set up supported mcs set */ + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + + switch(sc->rx_chainmask) { + case 1: + ht_info->mcs.rx_mask[0] = 0xff; + break; + case 3: + case 5: + case 7: + default: + ht_info->mcs.rx_mask[0] = 0xff; + ht_info->mcs.rx_mask[1] = 0xff; + break; + } + + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +} + +static void ath9k_bss_assoc_info(struct ath_softc *sc, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath_vif *avp = (void *)vif->drv_priv; + + if (bss_conf->assoc) { + DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d, bssid: %pM\n", + bss_conf->aid, sc->curbssid); + + /* New association, store aid */ + if (avp->av_opmode == NL80211_IFTYPE_STATION) { + sc->curaid = bss_conf->aid; + ath9k_hw_write_associd(sc); + + /* + * Request a re-configuration of Beacon related timers + * on the receipt of the first Beacon frame (i.e., + * after time sync with the AP). + */ + sc->sc_flags |= SC_OP_BEACON_SYNC; + } + + /* Configure the beacon */ + ath_beacon_config(sc, vif); + + /* Reset rssi stats */ + sc->nodestats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; + sc->nodestats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; + sc->nodestats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; + sc->nodestats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER; + + ath_start_ani(sc); + } else { + DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info DISASSOC\n"); + sc->curaid = 0; + } +} + +/********************************/ +/* LED functions */ +/********************************/ + +static void ath_led_blink_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + ath_led_blink_work.work); + + if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED)) + return; + + if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) || + (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0); + else + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, + (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0); + + queue_delayed_work(sc->hw->workqueue, &sc->ath_led_blink_work, + (sc->sc_flags & SC_OP_LED_ON) ? + msecs_to_jiffies(sc->led_off_duration) : + msecs_to_jiffies(sc->led_on_duration)); + + sc->led_on_duration = sc->led_on_cnt ? + max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) : + ATH_LED_ON_DURATION_IDLE; + sc->led_off_duration = sc->led_off_cnt ? + max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) : + ATH_LED_OFF_DURATION_IDLE; + sc->led_on_cnt = sc->led_off_cnt = 0; + if (sc->sc_flags & SC_OP_LED_ON) + sc->sc_flags &= ~SC_OP_LED_ON; + else + sc->sc_flags |= SC_OP_LED_ON; +} + +static void ath_led_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); + struct ath_softc *sc = led->sc; + + switch (brightness) { + case LED_OFF: + if (led->led_type == ATH_LED_ASSOC || + led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, + (led->led_type == ATH_LED_RADIO)); + sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; + if (led->led_type == ATH_LED_RADIO) + sc->sc_flags &= ~SC_OP_LED_ON; + } else { + sc->led_off_cnt++; + } + break; + case LED_FULL: + if (led->led_type == ATH_LED_ASSOC) { + sc->sc_flags |= SC_OP_LED_ASSOCIATED; + queue_delayed_work(sc->hw->workqueue, + &sc->ath_led_blink_work, 0); + } else if (led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0); + sc->sc_flags |= SC_OP_LED_ON; + } else { + sc->led_on_cnt++; + } + break; + default: + break; + } +} + +static int ath_register_led(struct ath_softc *sc, struct ath_led *led, + char *trigger) +{ + int ret; + + led->sc = sc; + led->led_cdev.name = led->name; + led->led_cdev.default_trigger = trigger; + led->led_cdev.brightness_set = ath_led_brightness; + + ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); + if (ret) + DPRINTF(sc, ATH_DBG_FATAL, + "Failed to register led:%s", led->name); + else + led->registered = 1; + return ret; +} + +static void ath_unregister_led(struct ath_led *led) +{ + if (led->registered) { + led_classdev_unregister(&led->led_cdev); + led->registered = 0; + } +} + +static void ath_deinit_leds(struct ath_softc *sc) +{ + cancel_delayed_work_sync(&sc->ath_led_blink_work); + ath_unregister_led(&sc->assoc_led); + sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; + ath_unregister_led(&sc->tx_led); + ath_unregister_led(&sc->rx_led); + ath_unregister_led(&sc->radio_led); + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); +} + +static void ath_init_leds(struct ath_softc *sc) +{ + char *trigger; + int ret; + + /* Configure gpio 1 for output */ + ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + /* LED off, active low */ + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); + + INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work); + + trigger = ieee80211_get_radio_led_name(sc->hw); + snprintf(sc->radio_led.name, sizeof(sc->radio_led.name), + "ath9k-%s::radio", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->radio_led, trigger); + sc->radio_led.led_type = ATH_LED_RADIO; + if (ret) + goto fail; + + trigger = ieee80211_get_assoc_led_name(sc->hw); + snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name), + "ath9k-%s::assoc", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->assoc_led, trigger); + sc->assoc_led.led_type = ATH_LED_ASSOC; + if (ret) + goto fail; + + trigger = ieee80211_get_tx_led_name(sc->hw); + snprintf(sc->tx_led.name, sizeof(sc->tx_led.name), + "ath9k-%s::tx", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->tx_led, trigger); + sc->tx_led.led_type = ATH_LED_TX; + if (ret) + goto fail; + + trigger = ieee80211_get_rx_led_name(sc->hw); + snprintf(sc->rx_led.name, sizeof(sc->rx_led.name), + "ath9k-%s::rx", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->rx_led, trigger); + sc->rx_led.led_type = ATH_LED_RX; + if (ret) + goto fail; + + return; + +fail: + ath_deinit_leds(sc); +} + +void ath_radio_enable(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_channel *channel = sc->hw->conf.channel; + int r; + + ath9k_ps_wakeup(sc); + ath9k_hw_configpcipowersave(ah, 0); + + spin_lock_bh(&sc->sc_resetlock); + r = ath9k_hw_reset(ah, ah->curchan, false); + if (r) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to reset channel %u (%uMhz) ", + "reset status %d\n", + channel->center_freq, r); + } + spin_unlock_bh(&sc->sc_resetlock); + + ath_update_txpow(sc); + if (ath_startrecv(sc) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to restart recv logic\n"); + return; + } + + if (sc->sc_flags & SC_OP_BEACONS) + ath_beacon_config(sc, NULL); /* restart beacons */ + + /* Re-Enable interrupts */ + ath9k_hw_set_interrupts(ah, sc->imask); + + /* Enable LED */ + ath9k_hw_cfg_output(ah, ATH_LED_PIN, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + ath9k_hw_set_gpio(ah, ATH_LED_PIN, 0); + + ieee80211_wake_queues(sc->hw); + ath9k_ps_restore(sc); +} + +void ath_radio_disable(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_channel *channel = sc->hw->conf.channel; + int r; + + ath9k_ps_wakeup(sc); + ieee80211_stop_queues(sc->hw); + + /* Disable LED */ + ath9k_hw_set_gpio(ah, ATH_LED_PIN, 1); + ath9k_hw_cfg_gpio_input(ah, ATH_LED_PIN); + + /* Disable interrupts */ + ath9k_hw_set_interrupts(ah, 0); + + ath_drain_all_txq(sc, false); /* clear pending tx frames */ + ath_stoprecv(sc); /* turn off frame recv */ + ath_flushrecv(sc); /* flush recv queue */ + + spin_lock_bh(&sc->sc_resetlock); + r = ath9k_hw_reset(ah, ah->curchan, false); + if (r) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to reset channel %u (%uMhz) " + "reset status %d\n", + channel->center_freq, r); + } + spin_unlock_bh(&sc->sc_resetlock); + + ath9k_hw_phy_disable(ah); + ath9k_hw_configpcipowersave(ah, 1); + ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); + ath9k_ps_restore(sc); +} + +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) + +/*******************/ +/* Rfkill */ +/*******************/ + +static bool ath_is_rfkill_set(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) == + ah->rfkill_polarity; +} + +/* s/w rfkill handlers */ +static int ath_rfkill_set_block(void *data, bool blocked) +{ + struct ath_softc *sc = data; + + if (blocked) + ath_radio_disable(sc); + else + ath_radio_enable(sc); + + return 0; +} + +static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data) +{ + struct ath_softc *sc = data; + bool blocked = !!ath_is_rfkill_set(sc); + + if (rfkill_set_hw_state(rfkill, blocked)) + ath_radio_disable(sc); + else + ath_radio_enable(sc); +} + +/* Init s/w rfkill */ +static int ath_init_sw_rfkill(struct ath_softc *sc) +{ + sc->rf_kill.ops.set_block = ath_rfkill_set_block; + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) + sc->rf_kill.ops.poll = ath_rfkill_poll_state; + + snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name), + "ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy)); + + sc->rf_kill.rfkill = rfkill_alloc(sc->rf_kill.rfkill_name, + wiphy_dev(sc->hw->wiphy), + RFKILL_TYPE_WLAN, + &sc->rf_kill.ops, sc); + if (!sc->rf_kill.rfkill) { + DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n"); + return -ENOMEM; + } + + return 0; +} + +/* Deinitialize rfkill */ +static void ath_deinit_rfkill(struct ath_softc *sc) +{ + if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) { + rfkill_unregister(sc->rf_kill.rfkill); + rfkill_destroy(sc->rf_kill.rfkill); + sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED; + } +} + +static int ath_start_rfkill_poll(struct ath_softc *sc) +{ + if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) { + if (rfkill_register(sc->rf_kill.rfkill)) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to register rfkill\n"); + rfkill_destroy(sc->rf_kill.rfkill); + + /* Deinitialize the device */ + ath_cleanup(sc); + return -EIO; + } else { + sc->sc_flags |= SC_OP_RFKILL_REGISTERED; + } + } + + return 0; +} +#endif /* CONFIG_RFKILL */ + +void ath_cleanup(struct ath_softc *sc) +{ + ath_detach(sc); + free_irq(sc->irq, sc); + ath_bus_cleanup(sc); + kfree(sc->sec_wiphy); + ieee80211_free_hw(sc->hw); +} + +void ath_detach(struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + int i = 0; + + ath9k_ps_wakeup(sc); + + DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n"); + +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) + ath_deinit_rfkill(sc); +#endif + ath_deinit_leds(sc); + cancel_work_sync(&sc->chan_work); + cancel_delayed_work_sync(&sc->wiphy_work); + + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + if (aphy == NULL) + continue; + sc->sec_wiphy[i] = NULL; + ieee80211_unregister_hw(aphy->hw); + ieee80211_free_hw(aphy->hw); + } + ieee80211_unregister_hw(hw); + ath_rx_cleanup(sc); + ath_tx_cleanup(sc); + + tasklet_kill(&sc->intr_tq); + tasklet_kill(&sc->bcon_tasklet); + + if (!(sc->sc_flags & SC_OP_INVALID)) + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + + /* cleanup tx queues */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); + + ath9k_hw_detach(sc->sc_ah); + ath9k_exit_debug(sc); + ath9k_ps_restore(sc); +} + +static int ath9k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_regulatory *reg = &sc->sc_ah->regulatory; + + return ath_reg_notifier_apply(wiphy, request, reg); +} + +static int ath_init(u16 devid, struct ath_softc *sc) +{ + struct ath_hw *ah = NULL; + int status; + int error = 0, i; + int csz = 0; + + /* XXX: hardware will not be ready until ath_open() being called */ + sc->sc_flags |= SC_OP_INVALID; + + if (ath9k_init_debug(sc) < 0) + printk(KERN_ERR "Unable to create debugfs files\n"); + + spin_lock_init(&sc->wiphy_lock); + spin_lock_init(&sc->sc_resetlock); + spin_lock_init(&sc->sc_serial_rw); + mutex_init(&sc->mutex); + tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); + tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, + (unsigned long)sc); + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + ath_read_cachesize(sc, &csz); + /* XXX assert csz is non-zero */ + sc->cachelsz = csz << 2; /* convert to bytes */ + + ah = ath9k_hw_attach(devid, sc, &status); + if (ah == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to attach hardware; HAL status %d\n", status); + error = -ENXIO; + goto bad; + } + sc->sc_ah = ah; + + /* Get the hardware key cache size. */ + sc->keymax = ah->caps.keycache_size; + if (sc->keymax > ATH_KEYMAX) { + DPRINTF(sc, ATH_DBG_ANY, + "Warning, using only %u entries in %u key cache\n", + ATH_KEYMAX, sc->keymax); + 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->keymax; i++) + ath9k_hw_keyreset(ah, (u16) i); + + if (error) + goto bad; + + /* default to MONITOR mode */ + sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR; + + /* Setup rate tables */ + + ath_rate_attach(sc); + ath_setup_rates(sc, IEEE80211_BAND_2GHZ); + ath_setup_rates(sc, IEEE80211_BAND_5GHZ); + + /* + * 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->beacon.beaconq = ath_beaconq_setup(ah); + if (sc->beacon.beaconq == -1) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to setup a beacon xmit queue\n"); + error = -EIO; + goto bad2; + } + sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); + if (sc->beacon.cabq == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to setup CAB xmit queue\n"); + error = -EIO; + goto bad2; + } + + sc->config.cabqReadytime = ATH_CABQ_READY_TIME; + ath_cabq_update(sc); + + for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++) + sc->tx.hwq_map[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, + "Unable to setup xmit queue for BK traffic\n"); + error = -EIO; + goto bad2; + } + + if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to setup xmit queue for BE traffic\n"); + error = -EIO; + goto bad2; + } + if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to setup xmit queue for VI traffic\n"); + error = -EIO; + goto bad2; + } + if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to setup xmit queue for VO traffic\n"); + error = -EIO; + goto bad2; + } + + /* Initializes the noise floor to a reasonable default value. + * Later on this will be updated during ANI processing. */ + + sc->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR; + setup_timer(&sc->ani.timer, ath_ani_calibrate, (unsigned long)sc); + + if (ath9k_hw_getcapability(ah, ATH9K_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, ATH9K_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, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL) + && ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_MIC, NULL) + && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT, + 0, NULL)) + sc->splitmic = 1; + + /* turn on mcast key search if possible */ + if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) + (void)ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1, + 1, NULL); + + sc->config.txpowlimit = ATH_TXPOWER_MAX; + + /* 11n Capabilities */ + if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + sc->sc_flags |= SC_OP_TXAGGR; + sc->sc_flags |= SC_OP_RXAGGR; + } + + sc->tx_chainmask = ah->caps.tx_chainmask; + sc->rx_chainmask = ah->caps.rx_chainmask; + + ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL); + sc->rx.defant = ath9k_hw_getdefantenna(ah); + + if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN); + + sc->beacon.slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */ + + /* initialize beacon slots */ + for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) { + sc->beacon.bslot[i] = NULL; + sc->beacon.bslot_aphy[i] = NULL; + } + + /* setup channels and rates */ + + sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable; + sc->sbands[IEEE80211_BAND_2GHZ].bitrates = + sc->rates[IEEE80211_BAND_2GHZ]; + sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; + sc->sbands[IEEE80211_BAND_2GHZ].n_channels = + ARRAY_SIZE(ath9k_2ghz_chantable); + + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) { + sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable; + sc->sbands[IEEE80211_BAND_5GHZ].bitrates = + sc->rates[IEEE80211_BAND_5GHZ]; + sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; + sc->sbands[IEEE80211_BAND_5GHZ].n_channels = + ARRAY_SIZE(ath9k_5ghz_chantable); + } + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BT_COEX) + ath9k_hw_btcoex_enable(sc->sc_ah); + + 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->tx.txq[i]); +bad: + if (ah) + ath9k_hw_detach(ah); + ath9k_exit_debug(sc); + + return error; +} + +void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) +{ + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK | + IEEE80211_HW_SPECTRUM_MGMT; + + if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || modparam_nohwcrypt) + hw->flags |= IEEE80211_HW_MFP_CAPABLE; + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + + hw->queues = 4; + hw->max_rates = 4; + hw->channel_change_time = 5000; + hw->max_listen_interval = 10; + hw->max_rate_tries = ATH_11N_TXMAXTRY; + hw->sta_data_size = sizeof(struct ath_node); + hw->vif_data_size = sizeof(struct ath_vif); + + hw->rate_control_algorithm = "ath9k_rate_control"; + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &sc->sbands[IEEE80211_BAND_2GHZ]; + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &sc->sbands[IEEE80211_BAND_5GHZ]; +} + +int ath_attach(u16 devid, struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + int error = 0, i; + struct ath_regulatory *reg; + + DPRINTF(sc, ATH_DBG_CONFIG, "Attach ATH hw\n"); + + error = ath_init(devid, sc); + if (error != 0) + return error; + + /* get mac address from hardware and set in mac80211 */ + + SET_IEEE80211_PERM_ADDR(hw, sc->sc_ah->macaddr); + + ath_set_hw_capab(sc, hw); + + error = ath_regd_init(&sc->sc_ah->regulatory, sc->hw->wiphy, + ath9k_reg_notifier); + if (error) + return error; + + reg = &sc->sc_ah->regulatory; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) + setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); + } + + /* initialize tx/rx engine */ + error = ath_tx_init(sc, ATH_TXBUF); + if (error != 0) + goto error_attach; + + error = ath_rx_init(sc, ATH_RXBUF); + if (error != 0) + goto error_attach; + +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) + /* Initialize s/w rfkill */ + error = ath_init_sw_rfkill(sc); + if (error) + goto error_attach; +#endif + + INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); + INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); + sc->wiphy_scheduler_int = msecs_to_jiffies(500); + + error = ieee80211_register_hw(hw); + + if (!ath_is_world_regd(reg)) { + error = regulatory_hint(hw->wiphy, reg->alpha2); + if (error) + goto error_attach; + } + + /* Initialize LED control */ + ath_init_leds(sc); + + + return 0; + +error_attach: + /* cleanup tx queues */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); + + ath9k_hw_detach(sc->sc_ah); + ath9k_exit_debug(sc); + + return error; +} + +int ath_reset(struct ath_softc *sc, bool retry_tx) +{ + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_hw *hw = sc->hw; + int r; + + ath9k_hw_set_interrupts(ah, 0); + ath_drain_all_txq(sc, retry_tx); + ath_stoprecv(sc); + ath_flushrecv(sc); + + spin_lock_bh(&sc->sc_resetlock); + r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false); + if (r) + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to reset hardware; reset status %d\n", r); + spin_unlock_bh(&sc->sc_resetlock); + + if (ath_startrecv(sc) != 0) + DPRINTF(sc, ATH_DBG_FATAL, "Unable to start recv logic\n"); + + /* + * 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_cache_conf_rate(sc, &hw->conf); + + ath_update_txpow(sc); + + if (sc->sc_flags & SC_OP_BEACONS) + ath_beacon_config(sc, NULL); /* restart beacons */ + + ath9k_hw_set_interrupts(ah, sc->imask); + + if (retry_tx) { + int i; + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + spin_lock_bh(&sc->tx.txq[i].axq_lock); + ath_txq_schedule(sc, &sc->tx.txq[i]); + spin_unlock_bh(&sc->tx.txq[i].axq_lock); + } + } + } + + return r; +} + +/* + * 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 DMA: %u buffers %u desc/buf\n", + name, nbuf, ndesc); + + INIT_LIST_HEAD(head); + /* ath_desc must be a multiple of DWORDs */ + if ((sizeof(struct ath_desc) % 4) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, "ath_desc not DWORD aligned\n"); + ASSERT((sizeof(struct ath_desc) % 4) == 0); + error = -ENOMEM; + goto fail; + } + + 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->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { + 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 = dma_alloc_coherent(sc->dev, dd->dd_desc_len, + &dd->dd_desc_paddr, GFP_KERNEL); + if (dd->dd_desc == NULL) { + error = -ENOMEM; + goto fail; + } + ds = dd->dd_desc; + DPRINTF(sc, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", + 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 = kzalloc(bsize, GFP_KERNEL); + if (bf == NULL) { + error = -ENOMEM; + goto fail2; + } + dd->dd_bufptr = bf; + + for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + + if (!(sc->sc_ah->caps.hw_caps & + ATH9K_HW_CAP_4KB_SPLITTRANS)) { + /* + * 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: + dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, + dd->dd_desc_paddr); +fail: + memset(dd, 0, sizeof(*dd)); + return error; +#undef ATH_DESC_4KB_BOUND_CHECK +#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED +#undef DS2PHYS +} + +void ath_descdma_cleanup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head) +{ + dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, + dd->dd_desc_paddr); + + INIT_LIST_HEAD(head); + kfree(dd->dd_bufptr); + memset(dd, 0, sizeof(*dd)); +} + +int ath_get_hal_qnum(u16 queue, struct ath_softc *sc) +{ + int qnum; + + switch (queue) { + case 0: + qnum = sc->tx.hwq_map[ATH9K_WME_AC_VO]; + break; + case 1: + qnum = sc->tx.hwq_map[ATH9K_WME_AC_VI]; + break; + case 2: + qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE]; + break; + case 3: + qnum = sc->tx.hwq_map[ATH9K_WME_AC_BK]; + break; + default: + qnum = sc->tx.hwq_map[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; +} + +/* XXX: Remove me once we don't depend on ath9k_channel for all + * this redundant data */ +void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, + struct ath9k_channel *ichan) +{ + struct ieee80211_channel *chan = hw->conf.channel; + struct ieee80211_conf *conf = &hw->conf; + + ichan->channel = chan->center_freq; + ichan->chan = chan; + + if (chan->band == IEEE80211_BAND_2GHZ) { + ichan->chanmode = CHANNEL_G; + ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM; + } else { + ichan->chanmode = CHANNEL_A; + ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM; + } + + sc->tx_chan_width = ATH9K_HT_MACMODE_20; + + if (conf_is_ht(conf)) { + if (conf_is_ht40(conf)) + sc->tx_chan_width = ATH9K_HT_MACMODE_2040; + + ichan->chanmode = ath_get_extchanmode(sc, chan, + conf->channel_type); + } +} + +/**********************/ +/* mac80211 callbacks */ +/**********************/ + +static int ath9k_start(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ieee80211_channel *curchan = hw->conf.channel; + struct ath9k_channel *init_channel; + int r, pos; + + DPRINTF(sc, ATH_DBG_CONFIG, "Starting driver with " + "initial channel: %d MHz\n", curchan->center_freq); + + mutex_lock(&sc->mutex); + + if (ath9k_wiphy_started(sc)) { + if (sc->chan_idx == curchan->hw_value) { + /* + * Already on the operational channel, the new wiphy + * can be marked active. + */ + aphy->state = ATH_WIPHY_ACTIVE; + ieee80211_wake_queues(hw); + } else { + /* + * Another wiphy is on another channel, start the new + * wiphy in paused state. + */ + aphy->state = ATH_WIPHY_PAUSED; + ieee80211_stop_queues(hw); + } + mutex_unlock(&sc->mutex); + return 0; + } + aphy->state = ATH_WIPHY_ACTIVE; + + /* setup initial channel */ + + pos = curchan->hw_value; + + sc->chan_idx = pos; + init_channel = &sc->sc_ah->channels[pos]; + ath9k_update_ichannel(sc, hw, init_channel); + + /* Reset SERDES registers */ + ath9k_hw_configpcipowersave(sc->sc_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. + */ + spin_lock_bh(&sc->sc_resetlock); + r = ath9k_hw_reset(sc->sc_ah, init_channel, false); + if (r) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to reset hardware; reset status %d " + "(freq %u MHz)\n", r, + curchan->center_freq); + spin_unlock_bh(&sc->sc_resetlock); + goto mutex_unlock; + } + 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, "Unable to start recv logic\n"); + r = -EIO; + goto mutex_unlock; + } + + /* Setup our intr mask. */ + sc->imask = ATH9K_INT_RX | ATH9K_INT_TX + | ATH9K_INT_RXEOL | ATH9K_INT_RXORN + | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_GTT) + sc->imask |= ATH9K_INT_GTT; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) + sc->imask |= ATH9K_INT_CST; + + ath_cache_conf_rate(sc, &hw->conf); + + sc->sc_flags &= ~SC_OP_INVALID; + + /* Disable BMISS interrupt when we're not associated */ + sc->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); + + ieee80211_wake_queues(hw); + +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) + r = ath_start_rfkill_poll(sc); +#endif + +mutex_unlock: + mutex_unlock(&sc->mutex); + + return r; +} + +static int ath9k_tx(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_tx_control txctl; + int hdrlen, padsize; + + if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) { + printk(KERN_DEBUG "ath9k: %s: TX in unexpected wiphy state " + "%d\n", wiphy_name(hw->wiphy), aphy->state); + goto exit; + } + + if (sc->hw->conf.flags & IEEE80211_CONF_PS) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + /* + * mac80211 does not set PM field for normal data frames, so we + * need to update that based on the current PS mode. + */ + if (ieee80211_is_data(hdr->frame_control) && + !ieee80211_is_nullfunc(hdr->frame_control) && + !ieee80211_has_pm(hdr->frame_control)) { + DPRINTF(sc, ATH_DBG_PS, "Add PM=1 for a TX frame " + "while in PS mode\n"); + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + } + } + + if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) { + /* + * We are using PS-Poll and mac80211 can request TX while in + * power save mode. Need to wake up hardware for the TX to be + * completed and if needed, also for RX of buffered frames. + */ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + ath9k_ps_wakeup(sc); + ath9k_hw_setrxabort(sc->sc_ah, 0); + if (ieee80211_is_pspoll(hdr->frame_control)) { + DPRINTF(sc, ATH_DBG_PS, "Sending PS-Poll to pick a " + "buffered frame\n"); + sc->sc_flags |= SC_OP_WAIT_FOR_PSPOLL_DATA; + } else { + DPRINTF(sc, ATH_DBG_PS, "Wake up to complete TX\n"); + sc->sc_flags |= SC_OP_WAIT_FOR_TX_ACK; + } + /* + * The actual restore operation will happen only after + * the sc_flags bit is cleared. We are just dropping + * the ps_usecount here. + */ + ath9k_ps_restore(sc); + } + + memset(&txctl, 0, sizeof(struct ath_tx_control)); + + /* + * As a temporary workaround, assign seq# here; this will likely need + * to be cleaned up to work better with Beacon transmission and virtual + * BSSes. + */ + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + sc->tx.seq_no += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); + } + + /* Add the padding after the header if this is not already done */ + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + if (hdrlen & 3) { + padsize = hdrlen % 4; + if (skb_headroom(skb) < padsize) + return -1; + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, hdrlen); + } + + /* Check if a tx queue is available */ + + txctl.txq = ath_test_get_txq(sc, skb); + if (!txctl.txq) + goto exit; + + DPRINTF(sc, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb); + + if (ath_tx_start(hw, skb, &txctl) != 0) { + DPRINTF(sc, ATH_DBG_XMIT, "TX failed\n"); + goto exit; + } + + return 0; +exit: + dev_kfree_skb_any(skb); + return 0; +} + +static void ath9k_stop(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + aphy->state = ATH_WIPHY_INACTIVE; + + if (sc->sc_flags & SC_OP_INVALID) { + DPRINTF(sc, ATH_DBG_ANY, "Device not present\n"); + return; + } + + mutex_lock(&sc->mutex); + + ieee80211_stop_queues(hw); + + if (ath9k_wiphy_started(sc)) { + mutex_unlock(&sc->mutex); + return; /* another wiphy still in use */ + } + + /* make sure h/w will not generate any interrupt + * before setting the invalid flag. */ + ath9k_hw_set_interrupts(sc->sc_ah, 0); + + if (!(sc->sc_flags & SC_OP_INVALID)) { + ath_drain_all_txq(sc, false); + ath_stoprecv(sc); + ath9k_hw_phy_disable(sc->sc_ah); + } else + sc->rx.rxlink = NULL; + + rfkill_pause_polling(sc->rf_kill.rfkill); + + /* disable HAL and put h/w to sleep */ + ath9k_hw_disable(sc->sc_ah); + ath9k_hw_configpcipowersave(sc->sc_ah, 1); + + sc->sc_flags |= SC_OP_INVALID; + + mutex_unlock(&sc->mutex); + + DPRINTF(sc, ATH_DBG_CONFIG, "Driver halt\n"); +} + +static int ath9k_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_vif *avp = (void *)conf->vif->drv_priv; + enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED; + int ret = 0; + + mutex_lock(&sc->mutex); + + if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) && + sc->nvifs > 0) { + ret = -ENOBUFS; + goto out; + } + + switch (conf->type) { + case NL80211_IFTYPE_STATION: + ic_opmode = NL80211_IFTYPE_STATION; + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + if (sc->nbcnvifs >= ATH_BCBUF) { + ret = -ENOBUFS; + goto out; + } + ic_opmode = conf->type; + break; + default: + DPRINTF(sc, ATH_DBG_FATAL, + "Interface type %d not yet supported\n", conf->type); + ret = -EOPNOTSUPP; + goto out; + } + + DPRINTF(sc, ATH_DBG_CONFIG, "Attach a VIF of type: %d\n", ic_opmode); + + /* Set the VIF opmode */ + avp->av_opmode = ic_opmode; + avp->av_bslot = -1; + + sc->nvifs++; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + ath9k_set_bssid_mask(hw); + + if (sc->nvifs > 1) + goto out; /* skip global settings for secondary vif */ + + if (ic_opmode == NL80211_IFTYPE_AP) { + ath9k_hw_set_tsfadjust(sc->sc_ah, 1); + sc->sc_flags |= SC_OP_TSF_RESET; + } + + /* Set the device opmode */ + sc->sc_ah->opmode = ic_opmode; + + /* + * Enable MIB interrupts when there are hardware phy counters. + * Note we only do this (at the moment) for station mode. + */ + if ((conf->type == NL80211_IFTYPE_STATION) || + (conf->type == NL80211_IFTYPE_ADHOC) || + (conf->type == NL80211_IFTYPE_MESH_POINT)) { + if (ath9k_hw_phycounters(sc->sc_ah)) + sc->imask |= ATH9K_INT_MIB; + sc->imask |= ATH9K_INT_TSFOOR; + } + + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); + + if (conf->type == NL80211_IFTYPE_AP) + ath_start_ani(sc); + +out: + mutex_unlock(&sc->mutex); + return ret; +} + +static void ath9k_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_vif *avp = (void *)conf->vif->drv_priv; + int i; + + DPRINTF(sc, ATH_DBG_CONFIG, "Detach Interface\n"); + + mutex_lock(&sc->mutex); + + /* Stop ANI */ + del_timer_sync(&sc->ani.timer); + + /* Reclaim beacon resources */ + if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) || + (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) || + (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) { + ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); + ath_beacon_return(sc, avp); + } + + sc->sc_flags &= ~SC_OP_BEACONS; + + for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) { + if (sc->beacon.bslot[i] == conf->vif) { + printk(KERN_DEBUG "%s: vif had allocated beacon " + "slot\n", __func__); + sc->beacon.bslot[i] = NULL; + sc->beacon.bslot_aphy[i] = NULL; + } + } + + sc->nvifs--; + + mutex_unlock(&sc->mutex); +} + +static int ath9k_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ieee80211_conf *conf = &hw->conf; + struct ath_hw *ah = sc->sc_ah; + + mutex_lock(&sc->mutex); + + if (changed & IEEE80211_CONF_CHANGE_PS) { + if (conf->flags & IEEE80211_CONF_PS) { + if (!(ah->caps.hw_caps & + ATH9K_HW_CAP_AUTOSLEEP)) { + if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) { + sc->imask |= ATH9K_INT_TIM_TIMER; + ath9k_hw_set_interrupts(sc->sc_ah, + sc->imask); + } + ath9k_hw_setrxabort(sc->sc_ah, 1); + } + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); + } else { + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + if (!(ah->caps.hw_caps & + ATH9K_HW_CAP_AUTOSLEEP)) { + ath9k_hw_setrxabort(sc->sc_ah, 0); + sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | + SC_OP_WAIT_FOR_CAB | + SC_OP_WAIT_FOR_PSPOLL_DATA | + SC_OP_WAIT_FOR_TX_ACK); + if (sc->imask & ATH9K_INT_TIM_TIMER) { + sc->imask &= ~ATH9K_INT_TIM_TIMER; + ath9k_hw_set_interrupts(sc->sc_ah, + sc->imask); + } + } + } + } + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + struct ieee80211_channel *curchan = hw->conf.channel; + int pos = curchan->hw_value; + + aphy->chan_idx = pos; + aphy->chan_is_ht = conf_is_ht(conf); + + if (aphy->state == ATH_WIPHY_SCAN || + aphy->state == ATH_WIPHY_ACTIVE) + ath9k_wiphy_pause_all_forced(sc, aphy); + else { + /* + * Do not change operational channel based on a paused + * wiphy changes. + */ + goto skip_chan_change; + } + + DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n", + curchan->center_freq); + + /* XXX: remove me eventualy */ + ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]); + + ath_update_chainmask(sc, conf_is_ht(conf)); + + if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { + DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n"); + mutex_unlock(&sc->mutex); + return -EINVAL; + } + } + +skip_chan_change: + if (changed & IEEE80211_CONF_CHANGE_POWER) + sc->config.txpowlimit = 2 * conf->power_level; + + mutex_unlock(&sc->mutex); + + return 0; +} + +#define SUPPORTED_FILTERS \ + (FIF_PROMISC_IN_BSS | \ + FIF_ALLMULTI | \ + FIF_CONTROL | \ + FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_FCSFAIL) + +/* FIXME: sc->sc_full_reset ? */ +static void ath9k_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + int mc_count, + struct dev_mc_list *mclist) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + u32 rfilt; + + changed_flags &= SUPPORTED_FILTERS; + *total_flags &= SUPPORTED_FILTERS; + + sc->rx.rxfilter = *total_flags; + ath9k_ps_wakeup(sc); + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(sc->sc_ah, rfilt); + ath9k_ps_restore(sc); + + DPRINTF(sc, ATH_DBG_CONFIG, "Set HW RX filter: 0x%x\n", sc->rx.rxfilter); +} + +static void ath9k_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + switch (cmd) { + case STA_NOTIFY_ADD: + ath_node_attach(sc, sta); + break; + case STA_NOTIFY_REMOVE: + ath_node_detach(sc, sta); + break; + default: + break; + } +} + +static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath9k_tx_queue_info qi; + int ret = 0, qnum; + + if (queue >= WME_NUM_AC) + return 0; + + mutex_lock(&sc->mutex); + + memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); + + qi.tqi_aifs = params->aifs; + qi.tqi_cwmin = params->cw_min; + qi.tqi_cwmax = params->cw_max; + qi.tqi_burstTime = params->txop; + qnum = ath_get_hal_qnum(queue, sc); + + DPRINTF(sc, ATH_DBG_CONFIG, + "Configure tx [queue/halq] [%d/%d], " + "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", + queue, qnum, params->aifs, params->cw_min, + params->cw_max, params->txop); + + ret = ath_txq_update(sc, qnum, &qi); + if (ret) + DPRINTF(sc, ATH_DBG_FATAL, "TXQ Update failed\n"); + + mutex_unlock(&sc->mutex); + + return ret; +} + +static int ath9k_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + int ret = 0; + + if (modparam_nohwcrypt) + return -ENOSPC; + + mutex_lock(&sc->mutex); + ath9k_ps_wakeup(sc); + DPRINTF(sc, ATH_DBG_CONFIG, "Set HW Key\n"); + + switch (cmd) { + case SET_KEY: + ret = ath_key_config(sc, vif, sta, key); + if (ret >= 0) { + key->hw_key_idx = ret; + /* push IV and Michael MIC generation to stack */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + if (key->alg == ALG_TKIP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + if (sc->sc_ah->sw_mgmt_crypto && key->alg == ALG_CCMP) + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + ret = 0; + } + break; + case DISABLE_KEY: + ath_key_delete(sc, key); + break; + default: + ret = -EINVAL; + } + + ath9k_ps_restore(sc); + mutex_unlock(&sc->mutex); + + return ret; +} + +static void ath9k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_hw *ah = sc->sc_ah; + struct ath_vif *avp = (void *)vif->drv_priv; + u32 rfilt = 0; + int error, i; + + mutex_lock(&sc->mutex); + + /* + * TODO: Need to decide which hw opmode to use for + * multi-interface cases + * XXX: This belongs into add_interface! + */ + if (vif->type == NL80211_IFTYPE_AP && + ah->opmode != NL80211_IFTYPE_AP) { + ah->opmode = NL80211_IFTYPE_STATION; + ath9k_hw_setopmode(ah); + memcpy(sc->curbssid, sc->sc_ah->macaddr, ETH_ALEN); + sc->curaid = 0; + ath9k_hw_write_associd(sc); + /* Request full reset to get hw opmode changed properly */ + sc->sc_flags |= SC_OP_FULL_RESET; + } + + if ((changed & BSS_CHANGED_BSSID) && + !is_zero_ether_addr(bss_conf->bssid)) { + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + /* Set BSSID */ + memcpy(sc->curbssid, bss_conf->bssid, ETH_ALEN); + memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN); + sc->curaid = 0; + ath9k_hw_write_associd(sc); + + /* Set aggregation protection mode parameters */ + sc->config.ath_aggr_prot = 0; + + DPRINTF(sc, ATH_DBG_CONFIG, + "RX filter 0x%x bssid %pM aid 0x%x\n", + rfilt, sc->curbssid, sc->curaid); + + /* need to reconfigure the beacon */ + sc->sc_flags &= ~SC_OP_BEACONS ; + + break; + default: + break; + } + } + + if ((vif->type == NL80211_IFTYPE_ADHOC) || + (vif->type == NL80211_IFTYPE_AP) || + (vif->type == NL80211_IFTYPE_MESH_POINT)) { + if ((changed & BSS_CHANGED_BEACON) || + (changed & BSS_CHANGED_BEACON_ENABLED && + bss_conf->enable_beacon)) { + /* + * Allocate and setup the beacon frame. + * + * Stop any previous beacon DMA. This may be + * necessary, for example, when an ibss merge + * causes reconfiguration; we may be called + * with beacon transmission active. + */ + ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); + + error = ath_beacon_alloc(aphy, vif); + if (!error) + ath_beacon_config(sc, vif); + } + } + + /* Check for WLAN_CAPABILITY_PRIVACY ? */ + if ((avp->av_opmode != NL80211_IFTYPE_STATION)) { + for (i = 0; i < IEEE80211_WEP_NKID; i++) + if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i)) + ath9k_hw_keysetmac(sc->sc_ah, + (u16)i, + sc->curbssid); + } + + /* Only legacy IBSS for now */ + if (vif->type == NL80211_IFTYPE_ADHOC) + ath_update_chainmask(sc, 0); + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", + bss_conf->use_short_preamble); + if (bss_conf->use_short_preamble) + sc->sc_flags |= SC_OP_PREAMBLE_SHORT; + else + sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT; + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n", + bss_conf->use_cts_prot); + if (bss_conf->use_cts_prot && + hw->conf.channel->band != IEEE80211_BAND_5GHZ) + sc->sc_flags |= SC_OP_PROTECT_ENABLE; + else + sc->sc_flags &= ~SC_OP_PROTECT_ENABLE; + } + + if (changed & BSS_CHANGED_ASSOC) { + DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", + bss_conf->assoc); + ath9k_bss_assoc_info(sc, vif, bss_conf); + } + + /* + * The HW TSF has to be reset when the beacon interval changes. + * We set the flag here, and ath_beacon_config_ap() would take this + * into account when it gets called through the subsequent + * config_interface() call - with IFCC_BEACON in the changed field. + */ + + if (changed & BSS_CHANGED_BEACON_INT) { + sc->sc_flags |= SC_OP_TSF_RESET; + sc->beacon_interval = bss_conf->beacon_int; + } + + mutex_unlock(&sc->mutex); +} + +static u64 ath9k_get_tsf(struct ieee80211_hw *hw) +{ + u64 tsf; + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + mutex_lock(&sc->mutex); + tsf = ath9k_hw_gettsf64(sc->sc_ah); + mutex_unlock(&sc->mutex); + + return tsf; +} + +static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + mutex_lock(&sc->mutex); + ath9k_hw_settsf64(sc->sc_ah, tsf); + mutex_unlock(&sc->mutex); +} + +static void ath9k_reset_tsf(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + mutex_lock(&sc->mutex); + ath9k_hw_reset_tsf(sc->sc_ah); + mutex_unlock(&sc->mutex); +} + +static int ath9k_ampdu_action(struct ieee80211_hw *hw, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, + u16 tid, u16 *ssn) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + int ret = 0; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + if (!(sc->sc_flags & SC_OP_RXAGGR)) + ret = -ENOTSUPP; + break; + case IEEE80211_AMPDU_RX_STOP: + break; + case IEEE80211_AMPDU_TX_START: + ret = ath_tx_aggr_start(sc, sta, tid, ssn); + if (ret < 0) + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to start TX aggregation\n"); + else + ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_STOP: + ret = ath_tx_aggr_stop(sc, sta, tid); + if (ret < 0) + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to stop TX aggregation\n"); + + ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ath_tx_aggr_resume(sc, sta, tid); + break; + default: + DPRINTF(sc, ATH_DBG_FATAL, "Unknown AMPDU action\n"); + } + + return ret; +} + +static void ath9k_sw_scan_start(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + if (ath9k_wiphy_scanning(sc)) { + printk(KERN_DEBUG "ath9k: Two wiphys trying to scan at the " + "same time\n"); + /* + * Do not allow the concurrent scanning state for now. This + * could be improved with scanning control moved into ath9k. + */ + return; + } + + aphy->state = ATH_WIPHY_SCAN; + ath9k_wiphy_pause_all_forced(sc, aphy); + + mutex_lock(&sc->mutex); + sc->sc_flags |= SC_OP_SCANNING; + mutex_unlock(&sc->mutex); +} + +static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + mutex_lock(&sc->mutex); + aphy->state = ATH_WIPHY_ACTIVE; + sc->sc_flags &= ~SC_OP_SCANNING; + sc->sc_flags |= SC_OP_FULL_RESET; + mutex_unlock(&sc->mutex); +} + +struct ieee80211_ops ath9k_ops = { + .tx = ath9k_tx, + .start = ath9k_start, + .stop = ath9k_stop, + .add_interface = ath9k_add_interface, + .remove_interface = ath9k_remove_interface, + .config = ath9k_config, + .configure_filter = ath9k_configure_filter, + .sta_notify = ath9k_sta_notify, + .conf_tx = ath9k_conf_tx, + .bss_info_changed = ath9k_bss_info_changed, + .set_key = ath9k_set_key, + .get_tsf = ath9k_get_tsf, + .set_tsf = ath9k_set_tsf, + .reset_tsf = ath9k_reset_tsf, + .ampdu_action = ath9k_ampdu_action, + .sw_scan_start = ath9k_sw_scan_start, + .sw_scan_complete = ath9k_sw_scan_complete, +}; + +static struct { + u32 version; + const char * name; +} ath_mac_bb_names[] = { + { AR_SREV_VERSION_5416_PCI, "5416" }, + { AR_SREV_VERSION_5416_PCIE, "5418" }, + { AR_SREV_VERSION_9100, "9100" }, + { AR_SREV_VERSION_9160, "9160" }, + { AR_SREV_VERSION_9280, "9280" }, + { AR_SREV_VERSION_9285, "9285" } +}; + +static struct { + u16 version; + const char * name; +} ath_rf_names[] = { + { 0, "5133" }, + { AR_RAD5133_SREV_MAJOR, "5133" }, + { AR_RAD5122_SREV_MAJOR, "5122" }, + { AR_RAD2133_SREV_MAJOR, "2133" }, + { AR_RAD2122_SREV_MAJOR, "2122" } +}; + +/* + * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown. + */ +const char * +ath_mac_bb_name(u32 mac_bb_version) +{ + int i; + + for (i=0; i<ARRAY_SIZE(ath_mac_bb_names); i++) { + if (ath_mac_bb_names[i].version == mac_bb_version) { + return ath_mac_bb_names[i].name; + } + } + + return "????"; +} + +/* + * Return the RF name. "????" is returned if the RF is unknown. + */ +const char * +ath_rf_name(u16 rf_version) +{ + int i; + + for (i=0; i<ARRAY_SIZE(ath_rf_names); i++) { + if (ath_rf_names[i].version == rf_version) { + return ath_rf_names[i].name; + } + } + + return "????"; +} + +static int __init ath9k_init(void) +{ + int error; + + /* Register rate control algorithm */ + error = ath_rate_control_register(); + if (error != 0) { + printk(KERN_ERR + "ath9k: Unable to register rate control " + "algorithm: %d\n", + error); + goto err_out; + } + + error = ath9k_debug_create_root(); + if (error) { + printk(KERN_ERR + "ath9k: Unable to create debugfs root: %d\n", + error); + goto err_rate_unregister; + } + + error = ath_pci_init(); + if (error < 0) { + printk(KERN_ERR + "ath9k: No PCI devices found, driver not installed.\n"); + error = -ENODEV; + goto err_remove_root; + } + + error = ath_ahb_init(); + if (error < 0) { + error = -ENODEV; + goto err_pci_exit; + } + + return 0; + + err_pci_exit: + ath_pci_exit(); + + err_remove_root: + ath9k_debug_remove_root(); + err_rate_unregister: + ath_rate_control_unregister(); + err_out: + return error; +} +module_init(ath9k_init); + +static void __exit ath9k_exit(void) +{ + ath_ahb_exit(); + ath_pci_exit(); + ath9k_debug_remove_root(); + ath_rate_control_unregister(); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} +module_exit(ath9k_exit); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c new file mode 100644 index 00000000000..ccdf20a2e9b --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2008-2009 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 <linux/nl80211.h> +#include <linux/pci.h> +#include "ath9k.h" + +static struct pci_device_id ath_pci_id_table[] __devinitdata = { + { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */ + { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */ + { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */ + { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ + { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ + { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */ + { 0 } +}; + +/* return bus cachesize in 4B word units */ +static void ath_pci_read_cachesize(struct ath_softc *sc, int *csz) +{ + u8 u8tmp; + + pci_read_config_byte(to_pci_dev(sc->dev), 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 */ +} + +static void ath_pci_cleanup(struct ath_softc *sc) +{ + struct pci_dev *pdev = to_pci_dev(sc->dev); + + pci_iounmap(pdev, sc->mem); + pci_disable_device(pdev); + pci_release_region(pdev, 0); +} + +static bool ath_pci_eeprom_read(struct ath_hw *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, + AH_WAIT_TIMEOUT)) { + return false; + } + + *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA), + AR_EEPROM_STATUS_DATA_VAL); + + return true; +} + +static struct ath_bus_ops ath_pci_bus_ops = { + .read_cachesize = ath_pci_read_cachesize, + .cleanup = ath_pci_cleanup, + .eeprom_read = ath_pci_eeprom_read, +}; + +static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + void __iomem *mem; + struct ath_wiphy *aphy; + struct ath_softc *sc; + struct ieee80211_hw *hw; + u8 csz; + int ret = 0; + struct ath_hw *ah; + + if (pci_enable_device(pdev)) + return -EIO; + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + + if (ret) { + printk(KERN_ERR "ath9k: 32-bit DMA not available\n"); + goto bad; + } + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + + if (ret) { + printk(KERN_ERR "ath9k: 32-bit DMA consistent " + "DMA enable failed\n"); + goto bad; + } + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); + if (csz == 0) { + /* + * Linux 2.4.18 (at least) writes the cache line size + * register as a 16-bit wide register which is wrong. + * We must have this setup properly for rx buffer + * DMA to work so force a reasonable value here if it + * comes up zero. + */ + csz = L1_CACHE_BYTES / sizeof(u32); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); + } + /* + * The default setting of latency timer yields poor results, + * set it to the value used by other systems. It may be worth + * tweaking this setting more. + */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); + + pci_set_master(pdev); + + ret = pci_request_region(pdev, 0, "ath9k"); + if (ret) { + dev_err(&pdev->dev, "PCI memory region reserve error\n"); + ret = -ENODEV; + goto bad; + } + + mem = pci_iomap(pdev, 0, 0); + if (!mem) { + printk(KERN_ERR "PCI memory map error\n") ; + ret = -EIO; + goto bad1; + } + + hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy) + + sizeof(struct ath_softc), &ath9k_ops); + if (hw == NULL) { + printk(KERN_ERR "ath_pci: no memory for ieee80211_hw\n"); + goto bad2; + } + + SET_IEEE80211_DEV(hw, &pdev->dev); + pci_set_drvdata(pdev, hw); + + aphy = hw->priv; + sc = (struct ath_softc *) (aphy + 1); + aphy->sc = sc; + aphy->hw = hw; + sc->pri_wiphy = aphy; + sc->hw = hw; + sc->dev = &pdev->dev; + sc->mem = mem; + sc->bus_ops = &ath_pci_bus_ops; + + if (ath_attach(id->device, sc) != 0) { + ret = -ENODEV; + goto bad3; + } + + /* setup interrupt service routine */ + + if (request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath", sc)) { + printk(KERN_ERR "%s: request_irq failed\n", + wiphy_name(hw->wiphy)); + ret = -EIO; + goto bad4; + } + + sc->irq = pdev->irq; + + ah = sc->sc_ah; + printk(KERN_INFO + "%s: Atheros AR%s MAC/BB Rev:%x " + "AR%s RF Rev:%x: mem=0x%lx, irq=%d\n", + wiphy_name(hw->wiphy), + ath_mac_bb_name(ah->hw_version.macVersion), + ah->hw_version.macRev, + ath_rf_name((ah->hw_version.analog5GhzRev & AR_RADIO_SREV_MAJOR)), + ah->hw_version.phyRev, + (unsigned long)mem, pdev->irq); + + return 0; +bad4: + ath_detach(sc); +bad3: + ieee80211_free_hw(hw); +bad2: + pci_iounmap(pdev, mem); +bad1: + pci_release_region(pdev, 0); +bad: + pci_disable_device(pdev); + return ret; +} + +static void ath_pci_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + ath_cleanup(sc); +} + +#ifdef CONFIG_PM + +static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int ath_pci_resume(struct pci_dev *pdev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + int err; + + err = pci_enable_device(pdev); + if (err) + return err; + pci_restore_state(pdev); + + /* Enable LED */ + ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); + + return 0; +} + +#endif /* CONFIG_PM */ + +MODULE_DEVICE_TABLE(pci, ath_pci_id_table); + +static struct pci_driver ath_pci_driver = { + .name = "ath9k", + .id_table = ath_pci_id_table, + .probe = ath_pci_probe, + .remove = ath_pci_remove, +#ifdef CONFIG_PM + .suspend = ath_pci_suspend, + .resume = ath_pci_resume, +#endif /* CONFIG_PM */ +}; + +int ath_pci_init(void) +{ + return pci_register_driver(&ath_pci_driver); +} + +void ath_pci_exit(void) +{ + pci_unregister_driver(&ath_pci_driver); +} diff --git a/drivers/net/wireless/ath/ath9k/phy.c b/drivers/net/wireless/ath/ath9k/phy.c new file mode 100644 index 00000000000..aaa941561c3 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/phy.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +void +ath9k_hw_write_regs(struct ath_hw *ah, u32 modesIndex, u32 freqIndex, + int regWrites) +{ + REG_WRITE_ARRAY(&ah->iniBB_RfGain, freqIndex, regWrites); +} + +bool +ath9k_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) +{ + u32 channelSel = 0; + u32 bModeSynth = 0; + u32 aModeRefSel = 0; + u32 reg32 = 0; + u16 freq; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = centers.synth_center; + + if (freq < 4800) { + u32 txctl; + + if (((freq - 2192) % 5) == 0) { + channelSel = ((freq - 672) * 2 - 3040) / 10; + bModeSynth = 0; + } else if (((freq - 2224) % 5) == 0) { + channelSel = ((freq - 704) * 2 - 3040) / 10; + bModeSynth = 1; + } else { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Invalid channel %u MHz\n", freq); + return false; + } + + channelSel = (channelSel << 2) & 0xff; + channelSel = ath9k_hw_reverse_bits(channelSel, 8); + + txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); + if (freq == 2484) { + + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl | AR_PHY_CCK_TX_CTRL_JAPAN); + } else { + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); + } + + } else if ((freq % 20) == 0 && freq >= 5120) { + channelSel = + ath9k_hw_reverse_bits(((freq - 4800) / 20 << 2), 8); + aModeRefSel = ath9k_hw_reverse_bits(1, 2); + } else if ((freq % 10) == 0) { + channelSel = + ath9k_hw_reverse_bits(((freq - 4800) / 10 << 1), 8); + if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) + aModeRefSel = ath9k_hw_reverse_bits(2, 2); + else + aModeRefSel = ath9k_hw_reverse_bits(1, 2); + } else if ((freq % 5) == 0) { + channelSel = ath9k_hw_reverse_bits((freq - 4800) / 5, 8); + aModeRefSel = ath9k_hw_reverse_bits(1, 2); + } else { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Invalid channel %u MHz\n", freq); + return false; + } + + reg32 = + (channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) | + (1 << 5) | 0x1; + + REG_WRITE(ah, AR_PHY(0x37), reg32); + + ah->curchan = chan; + ah->curchan_rad_index = -1; + + return true; +} + +void ath9k_hw_ar9280_set_channel(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + u16 bMode, fracMode, aModeRefSel = 0; + u32 freq, ndiv, channelSel = 0, channelFrac = 0, reg32 = 0; + struct chan_centers centers; + u32 refDivA = 24; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = centers.synth_center; + + reg32 = REG_READ(ah, AR_PHY_SYNTH_CONTROL); + reg32 &= 0xc0000000; + + if (freq < 4800) { + u32 txctl; + + bMode = 1; + fracMode = 1; + aModeRefSel = 0; + channelSel = (freq * 0x10000) / 15; + + txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); + if (freq == 2484) { + + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl | AR_PHY_CCK_TX_CTRL_JAPAN); + } else { + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); + } + } else { + bMode = 0; + fracMode = 0; + + switch(ah->eep_ops->get_eeprom(ah, EEP_FRAC_N_5G)) { + case 0: + if ((freq % 20) == 0) { + aModeRefSel = 3; + } else if ((freq % 10) == 0) { + aModeRefSel = 2; + } + if (aModeRefSel) + break; + case 1: + default: + aModeRefSel = 0; + fracMode = 1; + refDivA = 1; + channelSel = (freq * 0x8000) / 15; + + REG_RMW_FIELD(ah, AR_AN_SYNTH9, + AR_AN_SYNTH9_REFDIVA, refDivA); + + } + + if (!fracMode) { + ndiv = (freq * (refDivA >> aModeRefSel)) / 60; + channelSel = ndiv & 0x1ff; + channelFrac = (ndiv & 0xfffffe00) * 2; + channelSel = (channelSel << 17) | channelFrac; + } + } + + reg32 = reg32 | + (bMode << 29) | + (fracMode << 28) | (aModeRefSel << 26) | (channelSel); + + REG_WRITE(ah, AR_PHY_SYNTH_CONTROL, reg32); + + ah->curchan = chan; + ah->curchan_rad_index = -1; +} + +static void +ath9k_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32, + u32 numBits, u32 firstBit, + u32 column) +{ + u32 tmp32, mask, arrayEntry, lastBit; + int32_t bitPosition, bitsLeft; + + tmp32 = ath9k_hw_reverse_bits(reg32, numBits); + arrayEntry = (firstBit - 1) / 8; + bitPosition = (firstBit - 1) % 8; + bitsLeft = numBits; + while (bitsLeft > 0) { + lastBit = (bitPosition + bitsLeft > 8) ? + 8 : bitPosition + bitsLeft; + mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) << + (column * 8); + rfBuf[arrayEntry] &= ~mask; + rfBuf[arrayEntry] |= ((tmp32 << bitPosition) << + (column * 8)) & mask; + bitsLeft -= 8 - bitPosition; + tmp32 = tmp32 >> (8 - bitPosition); + bitPosition = 0; + arrayEntry++; + } +} + +bool +ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, + u16 modesIndex) +{ + u32 eepMinorRev; + u32 ob5GHz = 0, db5GHz = 0; + u32 ob2GHz = 0, db2GHz = 0; + int regWrites = 0; + + if (AR_SREV_9280_10_OR_LATER(ah)) + return true; + + eepMinorRev = ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV); + + RF_BANK_SETUP(ah->analogBank0Data, &ah->iniBank0, 1); + + RF_BANK_SETUP(ah->analogBank1Data, &ah->iniBank1, 1); + + RF_BANK_SETUP(ah->analogBank2Data, &ah->iniBank2, 1); + + RF_BANK_SETUP(ah->analogBank3Data, &ah->iniBank3, + modesIndex); + { + int i; + for (i = 0; i < ah->iniBank6TPC.ia_rows; i++) { + ah->analogBank6Data[i] = + INI_RA(&ah->iniBank6TPC, i, modesIndex); + } + } + + if (eepMinorRev >= 2) { + if (IS_CHAN_2GHZ(chan)) { + ob2GHz = ah->eep_ops->get_eeprom(ah, EEP_OB_2); + db2GHz = ah->eep_ops->get_eeprom(ah, EEP_DB_2); + ath9k_phy_modify_rx_buffer(ah->analogBank6Data, + ob2GHz, 3, 197, 0); + ath9k_phy_modify_rx_buffer(ah->analogBank6Data, + db2GHz, 3, 194, 0); + } else { + ob5GHz = ah->eep_ops->get_eeprom(ah, EEP_OB_5); + db5GHz = ah->eep_ops->get_eeprom(ah, EEP_DB_5); + ath9k_phy_modify_rx_buffer(ah->analogBank6Data, + ob5GHz, 3, 203, 0); + ath9k_phy_modify_rx_buffer(ah->analogBank6Data, + db5GHz, 3, 200, 0); + } + } + + RF_BANK_SETUP(ah->analogBank7Data, &ah->iniBank7, 1); + + REG_WRITE_RF_ARRAY(&ah->iniBank0, ah->analogBank0Data, + regWrites); + REG_WRITE_RF_ARRAY(&ah->iniBank1, ah->analogBank1Data, + regWrites); + REG_WRITE_RF_ARRAY(&ah->iniBank2, ah->analogBank2Data, + regWrites); + REG_WRITE_RF_ARRAY(&ah->iniBank3, ah->analogBank3Data, + regWrites); + REG_WRITE_RF_ARRAY(&ah->iniBank6TPC, ah->analogBank6Data, + regWrites); + REG_WRITE_RF_ARRAY(&ah->iniBank7, ah->analogBank7Data, + regWrites); + + return true; +} + +void +ath9k_hw_rfdetach(struct ath_hw *ah) +{ + if (ah->analogBank0Data != NULL) { + kfree(ah->analogBank0Data); + ah->analogBank0Data = NULL; + } + if (ah->analogBank1Data != NULL) { + kfree(ah->analogBank1Data); + ah->analogBank1Data = NULL; + } + if (ah->analogBank2Data != NULL) { + kfree(ah->analogBank2Data); + ah->analogBank2Data = NULL; + } + if (ah->analogBank3Data != NULL) { + kfree(ah->analogBank3Data); + ah->analogBank3Data = NULL; + } + if (ah->analogBank6Data != NULL) { + kfree(ah->analogBank6Data); + ah->analogBank6Data = NULL; + } + if (ah->analogBank6TPCData != NULL) { + kfree(ah->analogBank6TPCData); + ah->analogBank6TPCData = NULL; + } + if (ah->analogBank7Data != NULL) { + kfree(ah->analogBank7Data); + ah->analogBank7Data = NULL; + } + if (ah->addac5416_21 != NULL) { + kfree(ah->addac5416_21); + ah->addac5416_21 = NULL; + } + if (ah->bank6Temp != NULL) { + kfree(ah->bank6Temp); + ah->bank6Temp = NULL; + } +} + +bool ath9k_hw_init_rf(struct ath_hw *ah, int *status) +{ + if (!AR_SREV_9280_10_OR_LATER(ah)) { + ah->analogBank0Data = + kzalloc((sizeof(u32) * + ah->iniBank0.ia_rows), GFP_KERNEL); + ah->analogBank1Data = + kzalloc((sizeof(u32) * + ah->iniBank1.ia_rows), GFP_KERNEL); + ah->analogBank2Data = + kzalloc((sizeof(u32) * + ah->iniBank2.ia_rows), GFP_KERNEL); + ah->analogBank3Data = + kzalloc((sizeof(u32) * + ah->iniBank3.ia_rows), GFP_KERNEL); + ah->analogBank6Data = + kzalloc((sizeof(u32) * + ah->iniBank6.ia_rows), GFP_KERNEL); + ah->analogBank6TPCData = + kzalloc((sizeof(u32) * + ah->iniBank6TPC.ia_rows), GFP_KERNEL); + ah->analogBank7Data = + kzalloc((sizeof(u32) * + ah->iniBank7.ia_rows), GFP_KERNEL); + + if (ah->analogBank0Data == NULL + || ah->analogBank1Data == NULL + || ah->analogBank2Data == NULL + || ah->analogBank3Data == NULL + || ah->analogBank6Data == NULL + || ah->analogBank6TPCData == NULL + || ah->analogBank7Data == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Cannot allocate RF banks\n"); + *status = -ENOMEM; + return false; + } + + ah->addac5416_21 = + kzalloc((sizeof(u32) * + ah->iniAddac.ia_rows * + ah->iniAddac.ia_columns), GFP_KERNEL); + if (ah->addac5416_21 == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Cannot allocate addac5416_21\n"); + *status = -ENOMEM; + return false; + } + + ah->bank6Temp = + kzalloc((sizeof(u32) * + ah->iniBank6.ia_rows), GFP_KERNEL); + if (ah->bank6Temp == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Cannot allocate bank6Temp\n"); + *status = -ENOMEM; + return false; + } + } + + return true; +} + +void +ath9k_hw_decrease_chain_power(struct ath_hw *ah, struct ath9k_channel *chan) +{ + int i, regWrites = 0; + u32 bank6SelMask; + u32 *bank6Temp = ah->bank6Temp; + + switch (ah->diversity_control) { + case ATH9K_ANT_FIXED_A: + bank6SelMask = + (ah-> + antenna_switch_swap & ANTSWAP_AB) ? REDUCE_CHAIN_0 : + REDUCE_CHAIN_1; + break; + case ATH9K_ANT_FIXED_B: + bank6SelMask = + (ah-> + antenna_switch_swap & ANTSWAP_AB) ? REDUCE_CHAIN_1 : + REDUCE_CHAIN_0; + break; + case ATH9K_ANT_VARIABLE: + return; + break; + default: + return; + break; + } + + for (i = 0; i < ah->iniBank6.ia_rows; i++) + bank6Temp[i] = ah->analogBank6Data[i]; + + REG_WRITE(ah, AR_PHY_BASE + 0xD8, bank6SelMask); + + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 189, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 190, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 191, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 192, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 193, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 222, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 245, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 246, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 247, 0); + + REG_WRITE_RF_ARRAY(&ah->iniBank6, bank6Temp, regWrites); + + REG_WRITE(ah, AR_PHY_BASE + 0xD8, 0x00000053); +#ifdef ALTER_SWITCH + REG_WRITE(ah, PHY_SWITCH_CHAIN_0, + (REG_READ(ah, PHY_SWITCH_CHAIN_0) & ~0x38) + | ((REG_READ(ah, PHY_SWITCH_CHAIN_0) >> 3) & 0x38)); +#endif +} diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h new file mode 100644 index 00000000000..c70f530642f --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/phy.h @@ -0,0 +1,576 @@ +/* + * Copyright (c) 2008-2009 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 PHY_H +#define PHY_H + +void ath9k_hw_ar9280_set_channel(struct ath_hw *ah, + struct ath9k_channel + *chan); +bool ath9k_hw_set_channel(struct ath_hw *ah, + struct ath9k_channel *chan); +void ath9k_hw_write_regs(struct ath_hw *ah, u32 modesIndex, + u32 freqIndex, int regWrites); +bool ath9k_hw_set_rf_regs(struct ath_hw *ah, + struct ath9k_channel *chan, + u16 modesIndex); +void ath9k_hw_decrease_chain_power(struct ath_hw *ah, + struct ath9k_channel *chan); +bool ath9k_hw_init_rf(struct ath_hw *ah, + int *status); + +#define AR_PHY_BASE 0x9800 +#define AR_PHY(_n) (AR_PHY_BASE + ((_n)<<2)) + +#define AR_PHY_TEST 0x9800 +#define PHY_AGC_CLR 0x10000000 +#define RFSILENT_BB 0x00002000 + +#define AR_PHY_TURBO 0x9804 +#define AR_PHY_FC_TURBO_MODE 0x00000001 +#define AR_PHY_FC_TURBO_SHORT 0x00000002 +#define AR_PHY_FC_DYN2040_EN 0x00000004 +#define AR_PHY_FC_DYN2040_PRI_ONLY 0x00000008 +#define AR_PHY_FC_DYN2040_PRI_CH 0x00000010 +#define AR_PHY_FC_DYN2040_EXT_CH 0x00000020 +#define AR_PHY_FC_HT_EN 0x00000040 +#define AR_PHY_FC_SHORT_GI_40 0x00000080 +#define AR_PHY_FC_WALSH 0x00000100 +#define AR_PHY_FC_SINGLE_HT_LTF1 0x00000200 +#define AR_PHY_FC_ENABLE_DAC_FIFO 0x00000800 + +#define AR_PHY_TEST2 0x9808 + +#define AR_PHY_TIMING2 0x9810 +#define AR_PHY_TIMING3 0x9814 +#define AR_PHY_TIMING3_DSC_MAN 0xFFFE0000 +#define AR_PHY_TIMING3_DSC_MAN_S 17 +#define AR_PHY_TIMING3_DSC_EXP 0x0001E000 +#define AR_PHY_TIMING3_DSC_EXP_S 13 + +#define AR_PHY_CHIP_ID 0x9818 +#define AR_PHY_CHIP_ID_REV_0 0x80 +#define AR_PHY_CHIP_ID_REV_1 0x81 +#define AR_PHY_CHIP_ID_9160_REV_0 0xb0 + +#define AR_PHY_ACTIVE 0x981C +#define AR_PHY_ACTIVE_EN 0x00000001 +#define AR_PHY_ACTIVE_DIS 0x00000000 + +#define AR_PHY_RF_CTL2 0x9824 +#define AR_PHY_TX_END_DATA_START 0x000000FF +#define AR_PHY_TX_END_DATA_START_S 0 +#define AR_PHY_TX_END_PA_ON 0x0000FF00 +#define AR_PHY_TX_END_PA_ON_S 8 + +#define AR_PHY_RF_CTL3 0x9828 +#define AR_PHY_TX_END_TO_A2_RX_ON 0x00FF0000 +#define AR_PHY_TX_END_TO_A2_RX_ON_S 16 + +#define AR_PHY_ADC_CTL 0x982C +#define AR_PHY_ADC_CTL_OFF_INBUFGAIN 0x00000003 +#define AR_PHY_ADC_CTL_OFF_INBUFGAIN_S 0 +#define AR_PHY_ADC_CTL_OFF_PWDDAC 0x00002000 +#define AR_PHY_ADC_CTL_OFF_PWDBANDGAP 0x00004000 +#define AR_PHY_ADC_CTL_OFF_PWDADC 0x00008000 +#define AR_PHY_ADC_CTL_ON_INBUFGAIN 0x00030000 +#define AR_PHY_ADC_CTL_ON_INBUFGAIN_S 16 + +#define AR_PHY_ADC_SERIAL_CTL 0x9830 +#define AR_PHY_SEL_INTERNAL_ADDAC 0x00000000 +#define AR_PHY_SEL_EXTERNAL_RADIO 0x00000001 + +#define AR_PHY_RF_CTL4 0x9834 +#define AR_PHY_RF_CTL4_TX_END_XPAB_OFF 0xFF000000 +#define AR_PHY_RF_CTL4_TX_END_XPAB_OFF_S 24 +#define AR_PHY_RF_CTL4_TX_END_XPAA_OFF 0x00FF0000 +#define AR_PHY_RF_CTL4_TX_END_XPAA_OFF_S 16 +#define AR_PHY_RF_CTL4_FRAME_XPAB_ON 0x0000FF00 +#define AR_PHY_RF_CTL4_FRAME_XPAB_ON_S 8 +#define AR_PHY_RF_CTL4_FRAME_XPAA_ON 0x000000FF +#define AR_PHY_RF_CTL4_FRAME_XPAA_ON_S 0 + +#define AR_PHY_TSTDAC_CONST 0x983c + +#define AR_PHY_SETTLING 0x9844 +#define AR_PHY_SETTLING_SWITCH 0x00003F80 +#define AR_PHY_SETTLING_SWITCH_S 7 + +#define AR_PHY_RXGAIN 0x9848 +#define AR_PHY_RXGAIN_TXRX_ATTEN 0x0003F000 +#define AR_PHY_RXGAIN_TXRX_ATTEN_S 12 +#define AR_PHY_RXGAIN_TXRX_RF_MAX 0x007C0000 +#define AR_PHY_RXGAIN_TXRX_RF_MAX_S 18 +#define AR9280_PHY_RXGAIN_TXRX_ATTEN 0x00003F80 +#define AR9280_PHY_RXGAIN_TXRX_ATTEN_S 7 +#define AR9280_PHY_RXGAIN_TXRX_MARGIN 0x001FC000 +#define AR9280_PHY_RXGAIN_TXRX_MARGIN_S 14 + +#define AR_PHY_DESIRED_SZ 0x9850 +#define AR_PHY_DESIRED_SZ_ADC 0x000000FF +#define AR_PHY_DESIRED_SZ_ADC_S 0 +#define AR_PHY_DESIRED_SZ_PGA 0x0000FF00 +#define AR_PHY_DESIRED_SZ_PGA_S 8 +#define AR_PHY_DESIRED_SZ_TOT_DES 0x0FF00000 +#define AR_PHY_DESIRED_SZ_TOT_DES_S 20 + +#define AR_PHY_FIND_SIG 0x9858 +#define AR_PHY_FIND_SIG_FIRSTEP 0x0003F000 +#define AR_PHY_FIND_SIG_FIRSTEP_S 12 +#define AR_PHY_FIND_SIG_FIRPWR 0x03FC0000 +#define AR_PHY_FIND_SIG_FIRPWR_S 18 + +#define AR_PHY_AGC_CTL1 0x985C +#define AR_PHY_AGC_CTL1_COARSE_LOW 0x00007F80 +#define AR_PHY_AGC_CTL1_COARSE_LOW_S 7 +#define AR_PHY_AGC_CTL1_COARSE_HIGH 0x003F8000 +#define AR_PHY_AGC_CTL1_COARSE_HIGH_S 15 + +#define AR_PHY_AGC_CONTROL 0x9860 +#define AR_PHY_AGC_CONTROL_CAL 0x00000001 +#define AR_PHY_AGC_CONTROL_NF 0x00000002 +#define AR_PHY_AGC_CONTROL_ENABLE_NF 0x00008000 +#define AR_PHY_AGC_CONTROL_FLTR_CAL 0x00010000 +#define AR_PHY_AGC_CONTROL_NO_UPDATE_NF 0x00020000 + +#define AR_PHY_CCA 0x9864 +#define AR_PHY_MINCCA_PWR 0x0FF80000 +#define AR_PHY_MINCCA_PWR_S 19 +#define AR_PHY_CCA_THRESH62 0x0007F000 +#define AR_PHY_CCA_THRESH62_S 12 +#define AR9280_PHY_MINCCA_PWR 0x1FF00000 +#define AR9280_PHY_MINCCA_PWR_S 20 +#define AR9280_PHY_CCA_THRESH62 0x000FF000 +#define AR9280_PHY_CCA_THRESH62_S 12 + +#define AR_PHY_SFCORR_LOW 0x986C +#define AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW 0x00000001 +#define AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW 0x00003F00 +#define AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW_S 8 +#define AR_PHY_SFCORR_LOW_M1_THRESH_LOW 0x001FC000 +#define AR_PHY_SFCORR_LOW_M1_THRESH_LOW_S 14 +#define AR_PHY_SFCORR_LOW_M2_THRESH_LOW 0x0FE00000 +#define AR_PHY_SFCORR_LOW_M2_THRESH_LOW_S 21 + +#define AR_PHY_SFCORR 0x9868 +#define AR_PHY_SFCORR_M2COUNT_THR 0x0000001F +#define AR_PHY_SFCORR_M2COUNT_THR_S 0 +#define AR_PHY_SFCORR_M1_THRESH 0x00FE0000 +#define AR_PHY_SFCORR_M1_THRESH_S 17 +#define AR_PHY_SFCORR_M2_THRESH 0x7F000000 +#define AR_PHY_SFCORR_M2_THRESH_S 24 + +#define AR_PHY_SLEEP_CTR_CONTROL 0x9870 +#define AR_PHY_SLEEP_CTR_LIMIT 0x9874 +#define AR_PHY_SYNTH_CONTROL 0x9874 +#define AR_PHY_SLEEP_SCAL 0x9878 + +#define AR_PHY_PLL_CTL 0x987c +#define AR_PHY_PLL_CTL_40 0xaa +#define AR_PHY_PLL_CTL_40_5413 0x04 +#define AR_PHY_PLL_CTL_44 0xab +#define AR_PHY_PLL_CTL_44_2133 0xeb +#define AR_PHY_PLL_CTL_40_2133 0xea + +#define AR_PHY_RX_DELAY 0x9914 +#define AR_PHY_SEARCH_START_DELAY 0x9918 +#define AR_PHY_RX_DELAY_DELAY 0x00003FFF + +#define AR_PHY_TIMING_CTRL4(_i) (0x9920 + ((_i) << 12)) +#define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF 0x01F +#define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF_S 0 +#define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF 0x7E0 +#define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S 5 +#define AR_PHY_TIMING_CTRL4_IQCORR_ENABLE 0x800 +#define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX 0xF000 +#define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S 12 +#define AR_PHY_TIMING_CTRL4_DO_CAL 0x10000 + +#define AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI 0x80000000 +#define AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER 0x40000000 +#define AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK 0x20000000 +#define AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK 0x10000000 + +#define AR_PHY_TIMING5 0x9924 +#define AR_PHY_TIMING5_CYCPWR_THR1 0x000000FE +#define AR_PHY_TIMING5_CYCPWR_THR1_S 1 + +#define AR_PHY_POWER_TX_RATE1 0x9934 +#define AR_PHY_POWER_TX_RATE2 0x9938 +#define AR_PHY_POWER_TX_RATE_MAX 0x993c +#define AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE 0x00000040 + +#define AR_PHY_FRAME_CTL 0x9944 +#define AR_PHY_FRAME_CTL_TX_CLIP 0x00000038 +#define AR_PHY_FRAME_CTL_TX_CLIP_S 3 + +#define AR_PHY_TXPWRADJ 0x994C +#define AR_PHY_TXPWRADJ_CCK_GAIN_DELTA 0x00000FC0 +#define AR_PHY_TXPWRADJ_CCK_GAIN_DELTA_S 6 +#define AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX 0x00FC0000 +#define AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX_S 18 + +#define AR_PHY_RADAR_EXT 0x9940 +#define AR_PHY_RADAR_EXT_ENA 0x00004000 + +#define AR_PHY_RADAR_0 0x9954 +#define AR_PHY_RADAR_0_ENA 0x00000001 +#define AR_PHY_RADAR_0_FFT_ENA 0x80000000 +#define AR_PHY_RADAR_0_INBAND 0x0000003e +#define AR_PHY_RADAR_0_INBAND_S 1 +#define AR_PHY_RADAR_0_PRSSI 0x00000FC0 +#define AR_PHY_RADAR_0_PRSSI_S 6 +#define AR_PHY_RADAR_0_HEIGHT 0x0003F000 +#define AR_PHY_RADAR_0_HEIGHT_S 12 +#define AR_PHY_RADAR_0_RRSSI 0x00FC0000 +#define AR_PHY_RADAR_0_RRSSI_S 18 +#define AR_PHY_RADAR_0_FIRPWR 0x7F000000 +#define AR_PHY_RADAR_0_FIRPWR_S 24 + +#define AR_PHY_RADAR_1 0x9958 +#define AR_PHY_RADAR_1_RELPWR_ENA 0x00800000 +#define AR_PHY_RADAR_1_USE_FIR128 0x00400000 +#define AR_PHY_RADAR_1_RELPWR_THRESH 0x003F0000 +#define AR_PHY_RADAR_1_RELPWR_THRESH_S 16 +#define AR_PHY_RADAR_1_BLOCK_CHECK 0x00008000 +#define AR_PHY_RADAR_1_MAX_RRSSI 0x00004000 +#define AR_PHY_RADAR_1_RELSTEP_CHECK 0x00002000 +#define AR_PHY_RADAR_1_RELSTEP_THRESH 0x00001F00 +#define AR_PHY_RADAR_1_RELSTEP_THRESH_S 8 +#define AR_PHY_RADAR_1_MAXLEN 0x000000FF +#define AR_PHY_RADAR_1_MAXLEN_S 0 + +#define AR_PHY_SWITCH_CHAIN_0 0x9960 +#define AR_PHY_SWITCH_COM 0x9964 + +#define AR_PHY_SIGMA_DELTA 0x996C +#define AR_PHY_SIGMA_DELTA_ADC_SEL 0x00000003 +#define AR_PHY_SIGMA_DELTA_ADC_SEL_S 0 +#define AR_PHY_SIGMA_DELTA_FILT2 0x000000F8 +#define AR_PHY_SIGMA_DELTA_FILT2_S 3 +#define AR_PHY_SIGMA_DELTA_FILT1 0x00001F00 +#define AR_PHY_SIGMA_DELTA_FILT1_S 8 +#define AR_PHY_SIGMA_DELTA_ADC_CLIP 0x01FFE000 +#define AR_PHY_SIGMA_DELTA_ADC_CLIP_S 13 + +#define AR_PHY_RESTART 0x9970 +#define AR_PHY_RESTART_DIV_GC 0x001C0000 +#define AR_PHY_RESTART_DIV_GC_S 18 + +#define AR_PHY_RFBUS_REQ 0x997C +#define AR_PHY_RFBUS_REQ_EN 0x00000001 + +#define AR_PHY_TIMING7 0x9980 +#define AR_PHY_TIMING8 0x9984 +#define AR_PHY_TIMING8_PILOT_MASK_2 0x000FFFFF +#define AR_PHY_TIMING8_PILOT_MASK_2_S 0 + +#define AR_PHY_BIN_MASK2_1 0x9988 +#define AR_PHY_BIN_MASK2_2 0x998c +#define AR_PHY_BIN_MASK2_3 0x9990 +#define AR_PHY_BIN_MASK2_4 0x9994 + +#define AR_PHY_BIN_MASK_1 0x9900 +#define AR_PHY_BIN_MASK_2 0x9904 +#define AR_PHY_BIN_MASK_3 0x9908 + +#define AR_PHY_MASK_CTL 0x990c + +#define AR_PHY_BIN_MASK2_4_MASK_4 0x00003FFF +#define AR_PHY_BIN_MASK2_4_MASK_4_S 0 + +#define AR_PHY_TIMING9 0x9998 +#define AR_PHY_TIMING10 0x999c +#define AR_PHY_TIMING10_PILOT_MASK_2 0x000FFFFF +#define AR_PHY_TIMING10_PILOT_MASK_2_S 0 + +#define AR_PHY_TIMING11 0x99a0 +#define AR_PHY_TIMING11_SPUR_DELTA_PHASE 0x000FFFFF +#define AR_PHY_TIMING11_SPUR_DELTA_PHASE_S 0 +#define AR_PHY_TIMING11_SPUR_FREQ_SD 0x3FF00000 +#define AR_PHY_TIMING11_SPUR_FREQ_SD_S 20 +#define AR_PHY_TIMING11_USE_SPUR_IN_AGC 0x40000000 +#define AR_PHY_TIMING11_USE_SPUR_IN_SELFCOR 0x80000000 + +#define AR_PHY_RX_CHAINMASK 0x99a4 +#define AR_PHY_NEW_ADC_DC_GAIN_CORR(_i) (0x99b4 + ((_i) << 12)) +#define AR_PHY_NEW_ADC_GAIN_CORR_ENABLE 0x40000000 +#define AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE 0x80000000 +#define AR_PHY_MULTICHAIN_GAIN_CTL 0x99ac + +#define AR_PHY_EXT_CCA0 0x99b8 +#define AR_PHY_EXT_CCA0_THRESH62 0x000000FF +#define AR_PHY_EXT_CCA0_THRESH62_S 0 + +#define AR_PHY_EXT_CCA 0x99bc +#define AR_PHY_EXT_CCA_CYCPWR_THR1 0x0000FE00 +#define AR_PHY_EXT_CCA_CYCPWR_THR1_S 9 +#define AR_PHY_EXT_CCA_THRESH62 0x007F0000 +#define AR_PHY_EXT_CCA_THRESH62_S 16 +#define AR_PHY_EXT_MINCCA_PWR 0xFF800000 +#define AR_PHY_EXT_MINCCA_PWR_S 23 +#define AR9280_PHY_EXT_MINCCA_PWR 0x01FF0000 +#define AR9280_PHY_EXT_MINCCA_PWR_S 16 + +#define AR_PHY_SFCORR_EXT 0x99c0 +#define AR_PHY_SFCORR_EXT_M1_THRESH 0x0000007F +#define AR_PHY_SFCORR_EXT_M1_THRESH_S 0 +#define AR_PHY_SFCORR_EXT_M2_THRESH 0x00003F80 +#define AR_PHY_SFCORR_EXT_M2_THRESH_S 7 +#define AR_PHY_SFCORR_EXT_M1_THRESH_LOW 0x001FC000 +#define AR_PHY_SFCORR_EXT_M1_THRESH_LOW_S 14 +#define AR_PHY_SFCORR_EXT_M2_THRESH_LOW 0x0FE00000 +#define AR_PHY_SFCORR_EXT_M2_THRESH_LOW_S 21 +#define AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S 28 + +#define AR_PHY_HALFGI 0x99D0 +#define AR_PHY_HALFGI_DSC_MAN 0x0007FFF0 +#define AR_PHY_HALFGI_DSC_MAN_S 4 +#define AR_PHY_HALFGI_DSC_EXP 0x0000000F +#define AR_PHY_HALFGI_DSC_EXP_S 0 + +#define AR_PHY_CHAN_INFO_MEMORY 0x99DC +#define AR_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK 0x0001 + +#define AR_PHY_HEAVY_CLIP_ENABLE 0x99E0 + +#define AR_PHY_M_SLEEP 0x99f0 +#define AR_PHY_REFCLKDLY 0x99f4 +#define AR_PHY_REFCLKPD 0x99f8 + +#define AR_PHY_CALMODE 0x99f0 + +#define AR_PHY_CALMODE_IQ 0x00000000 +#define AR_PHY_CALMODE_ADC_GAIN 0x00000001 +#define AR_PHY_CALMODE_ADC_DC_PER 0x00000002 +#define AR_PHY_CALMODE_ADC_DC_INIT 0x00000003 + +#define AR_PHY_CAL_MEAS_0(_i) (0x9c10 + ((_i) << 12)) +#define AR_PHY_CAL_MEAS_1(_i) (0x9c14 + ((_i) << 12)) +#define AR_PHY_CAL_MEAS_2(_i) (0x9c18 + ((_i) << 12)) +#define AR_PHY_CAL_MEAS_3(_i) (0x9c1c + ((_i) << 12)) + +#define AR_PHY_CURRENT_RSSI 0x9c1c +#define AR9280_PHY_CURRENT_RSSI 0x9c3c + +#define AR_PHY_RFBUS_GRANT 0x9C20 +#define AR_PHY_RFBUS_GRANT_EN 0x00000001 + +#define AR_PHY_CHAN_INFO_GAIN_DIFF 0x9CF4 +#define AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320 + +#define AR_PHY_CHAN_INFO_GAIN 0x9CFC + +#define AR_PHY_MODE 0xA200 +#define AR_PHY_MODE_AR2133 0x08 +#define AR_PHY_MODE_AR5111 0x00 +#define AR_PHY_MODE_AR5112 0x08 +#define AR_PHY_MODE_DYNAMIC 0x04 +#define AR_PHY_MODE_RF2GHZ 0x02 +#define AR_PHY_MODE_RF5GHZ 0x00 +#define AR_PHY_MODE_CCK 0x01 +#define AR_PHY_MODE_OFDM 0x00 +#define AR_PHY_MODE_DYN_CCK_DISABLE 0x100 + +#define AR_PHY_CCK_TX_CTRL 0xA204 +#define AR_PHY_CCK_TX_CTRL_JAPAN 0x00000010 +#define AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK 0x0000000C +#define AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK_S 2 + +#define AR_PHY_CCK_DETECT 0xA208 +#define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK 0x0000003F +#define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S 0 +/* [12:6] settling time for antenna switch */ +#define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME 0x00001FC0 +#define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME_S 6 +#define AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV 0x2000 + +#define AR_PHY_GAIN_2GHZ 0xA20C +#define AR_PHY_GAIN_2GHZ_RXTX_MARGIN 0x00FC0000 +#define AR_PHY_GAIN_2GHZ_RXTX_MARGIN_S 18 +#define AR_PHY_GAIN_2GHZ_BSW_MARGIN 0x00003C00 +#define AR_PHY_GAIN_2GHZ_BSW_MARGIN_S 10 +#define AR_PHY_GAIN_2GHZ_BSW_ATTEN 0x0000001F +#define AR_PHY_GAIN_2GHZ_BSW_ATTEN_S 0 + +#define AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN 0x003E0000 +#define AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN_S 17 +#define AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN 0x0001F000 +#define AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN_S 12 +#define AR_PHY_GAIN_2GHZ_XATTEN2_DB 0x00000FC0 +#define AR_PHY_GAIN_2GHZ_XATTEN2_DB_S 6 +#define AR_PHY_GAIN_2GHZ_XATTEN1_DB 0x0000003F +#define AR_PHY_GAIN_2GHZ_XATTEN1_DB_S 0 + +#define AR_PHY_CCK_RXCTRL4 0xA21C +#define AR_PHY_CCK_RXCTRL4_FREQ_EST_SHORT 0x01F80000 +#define AR_PHY_CCK_RXCTRL4_FREQ_EST_SHORT_S 19 + +#define AR_PHY_DAG_CTRLCCK 0xA228 +#define AR_PHY_DAG_CTRLCCK_EN_RSSI_THR 0x00000200 +#define AR_PHY_DAG_CTRLCCK_RSSI_THR 0x0001FC00 +#define AR_PHY_DAG_CTRLCCK_RSSI_THR_S 10 + +#define AR_PHY_FORCE_CLKEN_CCK 0xA22C +#define AR_PHY_FORCE_CLKEN_CCK_MRC_MUX 0x00000040 + +#define AR_PHY_POWER_TX_RATE3 0xA234 +#define AR_PHY_POWER_TX_RATE4 0xA238 + +#define AR_PHY_SCRM_SEQ_XR 0xA23C +#define AR_PHY_HEADER_DETECT_XR 0xA240 +#define AR_PHY_CHIRP_DETECTED_XR 0xA244 +#define AR_PHY_BLUETOOTH 0xA254 + +#define AR_PHY_TPCRG1 0xA258 +#define AR_PHY_TPCRG1_NUM_PD_GAIN 0x0000c000 +#define AR_PHY_TPCRG1_NUM_PD_GAIN_S 14 + +#define AR_PHY_TPCRG1_PD_GAIN_1 0x00030000 +#define AR_PHY_TPCRG1_PD_GAIN_1_S 16 +#define AR_PHY_TPCRG1_PD_GAIN_2 0x000C0000 +#define AR_PHY_TPCRG1_PD_GAIN_2_S 18 +#define AR_PHY_TPCRG1_PD_GAIN_3 0x00300000 +#define AR_PHY_TPCRG1_PD_GAIN_3_S 20 + +#define AR_PHY_TPCRG1_PD_CAL_ENABLE 0x00400000 +#define AR_PHY_TPCRG1_PD_CAL_ENABLE_S 22 + +#define AR_PHY_TX_PWRCTRL4 0xa264 +#define AR_PHY_TX_PWRCTRL_PD_AVG_VALID 0x00000001 +#define AR_PHY_TX_PWRCTRL_PD_AVG_VALID_S 0 +#define AR_PHY_TX_PWRCTRL_PD_AVG_OUT 0x000001FE +#define AR_PHY_TX_PWRCTRL_PD_AVG_OUT_S 1 + +#define AR_PHY_TX_PWRCTRL6_0 0xa270 +#define AR_PHY_TX_PWRCTRL6_1 0xb270 +#define AR_PHY_TX_PWRCTRL_ERR_EST_MODE 0x03000000 +#define AR_PHY_TX_PWRCTRL_ERR_EST_MODE_S 24 + +#define AR_PHY_TX_PWRCTRL7 0xa274 +#define AR_PHY_TX_PWRCTRL_INIT_TX_GAIN 0x01F80000 +#define AR_PHY_TX_PWRCTRL_INIT_TX_GAIN_S 19 + +#define AR_PHY_TX_PWRCTRL9 0xa27C +#define AR_PHY_TX_DESIRED_SCALE_CCK 0x00007C00 +#define AR_PHY_TX_DESIRED_SCALE_CCK_S 10 + +#define AR_PHY_TX_GAIN_TBL1 0xa300 +#define AR_PHY_TX_GAIN 0x0007F000 +#define AR_PHY_TX_GAIN_S 12 + +#define AR_PHY_VIT_MASK2_M_46_61 0xa3a0 +#define AR_PHY_MASK2_M_31_45 0xa3a4 +#define AR_PHY_MASK2_M_16_30 0xa3a8 +#define AR_PHY_MASK2_M_00_15 0xa3ac +#define AR_PHY_MASK2_P_15_01 0xa3b8 +#define AR_PHY_MASK2_P_30_16 0xa3bc +#define AR_PHY_MASK2_P_45_31 0xa3c0 +#define AR_PHY_MASK2_P_61_45 0xa3c4 +#define AR_PHY_SPUR_REG 0x994c + +#define AR_PHY_SPUR_REG_MASK_RATE_CNTL (0xFF << 18) +#define AR_PHY_SPUR_REG_MASK_RATE_CNTL_S 18 + +#define AR_PHY_SPUR_REG_ENABLE_MASK_PPM 0x20000 +#define AR_PHY_SPUR_REG_MASK_RATE_SELECT (0xFF << 9) +#define AR_PHY_SPUR_REG_MASK_RATE_SELECT_S 9 +#define AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI 0x100 +#define AR_PHY_SPUR_REG_SPUR_RSSI_THRESH 0x7F +#define AR_PHY_SPUR_REG_SPUR_RSSI_THRESH_S 0 + +#define AR_PHY_PILOT_MASK_01_30 0xa3b0 +#define AR_PHY_PILOT_MASK_31_60 0xa3b4 + +#define AR_PHY_CHANNEL_MASK_01_30 0x99d4 +#define AR_PHY_CHANNEL_MASK_31_60 0x99d8 + +#define AR_PHY_ANALOG_SWAP 0xa268 +#define AR_PHY_SWAP_ALT_CHAIN 0x00000040 + +#define AR_PHY_TPCRG5 0xA26C +#define AR_PHY_TPCRG5_PD_GAIN_OVERLAP 0x0000000F +#define AR_PHY_TPCRG5_PD_GAIN_OVERLAP_S 0 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1 0x000003F0 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1_S 4 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2 0x0000FC00 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2_S 10 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3 0x003F0000 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3_S 16 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4 0x0FC00000 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4_S 22 + +/* Carrier leak calibration control, do it after AGC calibration */ +#define AR_PHY_CL_CAL_CTL 0xA358 +#define AR_PHY_CL_CAL_ENABLE 0x00000002 +#define AR_PHY_PARALLEL_CAL_ENABLE 0x00000001 + +#define AR_PHY_POWER_TX_RATE5 0xA38C +#define AR_PHY_POWER_TX_RATE6 0xA390 + +#define AR_PHY_CAL_CHAINMASK 0xA39C + +#define AR_PHY_POWER_TX_SUB 0xA3C8 +#define AR_PHY_POWER_TX_RATE7 0xA3CC +#define AR_PHY_POWER_TX_RATE8 0xA3D0 +#define AR_PHY_POWER_TX_RATE9 0xA3D4 + +#define AR_PHY_XPA_CFG 0xA3D8 +#define AR_PHY_FORCE_XPA_CFG 0x000000001 +#define AR_PHY_FORCE_XPA_CFG_S 0 + +#define AR_PHY_CH1_CCA 0xa864 +#define AR_PHY_CH1_MINCCA_PWR 0x0FF80000 +#define AR_PHY_CH1_MINCCA_PWR_S 19 +#define AR9280_PHY_CH1_MINCCA_PWR 0x1FF00000 +#define AR9280_PHY_CH1_MINCCA_PWR_S 20 + +#define AR_PHY_CH2_CCA 0xb864 +#define AR_PHY_CH2_MINCCA_PWR 0x0FF80000 +#define AR_PHY_CH2_MINCCA_PWR_S 19 + +#define AR_PHY_CH1_EXT_CCA 0xa9bc +#define AR_PHY_CH1_EXT_MINCCA_PWR 0xFF800000 +#define AR_PHY_CH1_EXT_MINCCA_PWR_S 23 +#define AR9280_PHY_CH1_EXT_MINCCA_PWR 0x01FF0000 +#define AR9280_PHY_CH1_EXT_MINCCA_PWR_S 16 + +#define AR_PHY_CH2_EXT_CCA 0xb9bc +#define AR_PHY_CH2_EXT_MINCCA_PWR 0xFF800000 +#define AR_PHY_CH2_EXT_MINCCA_PWR_S 23 + +#define REG_WRITE_RF_ARRAY(iniarray, regData, regWr) do { \ + int r; \ + for (r = 0; r < ((iniarray)->ia_rows); r++) { \ + REG_WRITE(ah, INI_RA((iniarray), r, 0), (regData)[r]); \ + DO_DELAY(regWr); \ + } \ + } while (0) + +#define ATH9K_IS_MIC_ENABLED(ah) \ + ((ah)->sta_id1_defaults & AR_STA_ID1_CRPT_MIC_ENABLE) + +#define ANTSWAP_AB 0x0001 +#define REDUCE_CHAIN_0 0x00000050 +#define REDUCE_CHAIN_1 0x00000051 + +#define RF_BANK_SETUP(_bank, _iniarray, _col) do { \ + int i; \ + for (i = 0; i < (_iniarray)->ia_rows; i++) \ + (_bank)[i] = INI_RA((_iniarray), i, _col);; \ + } while (0) + +#endif diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c new file mode 100644 index 00000000000..ba06e78b2f5 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -0,0 +1,1756 @@ +/* + * Copyright (c) 2004 Video54 Technologies, Inc. + * Copyright (c) 2004-2009 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 "ath9k.h" + +static const struct ath_rate_table ar5416_11na_ratetable = { + 42, + { + { VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ + 5400, 0x0b, 0x00, 12, + 0, 2, 1, 0, 0, 0, 0, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ + 7800, 0x0f, 0x00, 18, + 0, 3, 1, 1, 1, 1, 1, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ + 10000, 0x0a, 0x00, 24, + 2, 4, 2, 2, 2, 2, 2, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ + 13900, 0x0e, 0x00, 36, + 2, 6, 2, 3, 3, 3, 3, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ + 17300, 0x09, 0x00, 48, + 4, 10, 3, 4, 4, 4, 4, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ + 23000, 0x0d, 0x00, 72, + 4, 14, 3, 5, 5, 5, 5, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ + 27400, 0x08, 0x00, 96, + 4, 20, 3, 6, 6, 6, 6, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ + 29300, 0x0c, 0x00, 108, + 4, 23, 3, 7, 7, 7, 7, 0 }, + { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */ + 6400, 0x80, 0x00, 0, + 0, 2, 3, 8, 24, 8, 24, 3216 }, + { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */ + 12700, 0x81, 0x00, 1, + 2, 4, 3, 9, 25, 9, 25, 6434 }, + { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */ + 18800, 0x82, 0x00, 2, + 2, 6, 3, 10, 26, 10, 26, 9650 }, + { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */ + 25000, 0x83, 0x00, 3, + 4, 10, 3, 11, 27, 11, 27, 12868 }, + { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */ + 36700, 0x84, 0x00, 4, + 4, 14, 3, 12, 28, 12, 28, 19304 }, + { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */ + 48100, 0x85, 0x00, 5, + 4, 20, 3, 13, 29, 13, 29, 25740 }, + { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */ + 53500, 0x86, 0x00, 6, + 4, 23, 3, 14, 30, 14, 30, 28956 }, + { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */ + 59000, 0x87, 0x00, 7, + 4, 25, 3, 15, 31, 15, 32, 32180 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */ + 12700, 0x88, 0x00, + 8, 0, 2, 3, 16, 33, 16, 33, 6430 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */ + 24800, 0x89, 0x00, 9, + 2, 4, 3, 17, 34, 17, 34, 12860 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */ + 36600, 0x8a, 0x00, 10, + 2, 6, 3, 18, 35, 18, 35, 19300 }, + { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */ + 48100, 0x8b, 0x00, 11, + 4, 10, 3, 19, 36, 19, 36, 25736 }, + { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */ + 69500, 0x8c, 0x00, 12, + 4, 14, 3, 20, 37, 20, 37, 38600 }, + { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */ + 89500, 0x8d, 0x00, 13, + 4, 20, 3, 21, 38, 21, 38, 51472 }, + { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */ + 98900, 0x8e, 0x00, 14, + 4, 23, 3, 22, 39, 22, 39, 57890 }, + { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */ + 108300, 0x8f, 0x00, 15, + 4, 25, 3, 23, 40, 23, 41, 64320 }, + { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */ + 13200, 0x80, 0x00, 0, + 0, 2, 3, 8, 24, 24, 24, 6684 }, + { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */ + 25900, 0x81, 0x00, 1, + 2, 4, 3, 9, 25, 25, 25, 13368 }, + { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */ + 38600, 0x82, 0x00, 2, + 2, 6, 3, 10, 26, 26, 26, 20052 }, + { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */ + 49800, 0x83, 0x00, 3, + 4, 10, 3, 11, 27, 27, 27, 26738 }, + { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */ + 72200, 0x84, 0x00, 4, + 4, 14, 3, 12, 28, 28, 28, 40104 }, + { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */ + 92900, 0x85, 0x00, 5, + 4, 20, 3, 13, 29, 29, 29, 53476 }, + { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */ + 102700, 0x86, 0x00, 6, + 4, 23, 3, 14, 30, 30, 30, 60156 }, + { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */ + 112000, 0x87, 0x00, 7, + 4, 25, 3, 15, 31, 32, 32, 66840 }, + { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */ + 122000, 0x87, 0x00, 7, + 4, 25, 3, 15, 31, 32, 32, 74200 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */ + 25800, 0x88, 0x00, 8, + 0, 2, 3, 16, 33, 33, 33, 13360 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */ + 49800, 0x89, 0x00, 9, + 2, 4, 3, 17, 34, 34, 34, 26720 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */ + 71900, 0x8a, 0x00, 10, + 2, 6, 3, 18, 35, 35, 35, 40080 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */ + 92500, 0x8b, 0x00, 11, + 4, 10, 3, 19, 36, 36, 36, 53440 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */ + 130300, 0x8c, 0x00, 12, + 4, 14, 3, 20, 37, 37, 37, 80160 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */ + 162800, 0x8d, 0x00, 13, + 4, 20, 3, 21, 38, 38, 38, 106880 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */ + 178200, 0x8e, 0x00, 14, + 4, 23, 3, 22, 39, 39, 39, 120240 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */ + 192100, 0x8f, 0x00, 15, + 4, 25, 3, 23, 40, 41, 41, 133600 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */ + 207000, 0x8f, 0x00, 15, + 4, 25, 3, 23, 40, 41, 41, 148400 }, + }, + 50, /* probe interval */ + 50, /* rssi reduce interval */ + WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ +}; + +/* 4ms frame limit not used for NG mode. The values filled + * for HT are the 64K max aggregate limit */ + +static const struct ath_rate_table ar5416_11ng_ratetable = { + 46, + { + { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ + 900, 0x1b, 0x00, 2, + 0, 0, 1, 0, 0, 0, 0, 0 }, + { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */ + 1900, 0x1a, 0x04, 4, + 1, 1, 1, 1, 1, 1, 1, 0 }, + { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */ + 4900, 0x19, 0x04, 11, + 2, 2, 2, 2, 2, 2, 2, 0 }, + { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */ + 8100, 0x18, 0x04, 22, + 3, 3, 2, 3, 3, 3, 3, 0 }, + { INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ + 5400, 0x0b, 0x00, 12, + 4, 2, 1, 4, 4, 4, 4, 0 }, + { INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ + 7800, 0x0f, 0x00, 18, + 4, 3, 1, 5, 5, 5, 5, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ + 10100, 0x0a, 0x00, 24, + 6, 4, 1, 6, 6, 6, 6, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ + 14100, 0x0e, 0x00, 36, + 6, 6, 2, 7, 7, 7, 7, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ + 17700, 0x09, 0x00, 48, + 8, 10, 3, 8, 8, 8, 8, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ + 23700, 0x0d, 0x00, 72, + 8, 14, 3, 9, 9, 9, 9, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ + 27400, 0x08, 0x00, 96, + 8, 20, 3, 10, 10, 10, 10, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ + 30900, 0x0c, 0x00, 108, + 8, 23, 3, 11, 11, 11, 11, 0 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */ + 6400, 0x80, 0x00, 0, + 4, 2, 3, 12, 28, 12, 28, 3216 }, + { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */ + 12700, 0x81, 0x00, 1, + 6, 4, 3, 13, 29, 13, 29, 6434 }, + { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */ + 18800, 0x82, 0x00, 2, + 6, 6, 3, 14, 30, 14, 30, 9650 }, + { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */ + 25000, 0x83, 0x00, 3, + 8, 10, 3, 15, 31, 15, 31, 12868 }, + { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */ + 36700, 0x84, 0x00, 4, + 8, 14, 3, 16, 32, 16, 32, 19304 }, + { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */ + 48100, 0x85, 0x00, 5, + 8, 20, 3, 17, 33, 17, 33, 25740 }, + { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */ + 53500, 0x86, 0x00, 6, + 8, 23, 3, 18, 34, 18, 34, 28956 }, + { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */ + 59000, 0x87, 0x00, 7, + 8, 25, 3, 19, 35, 19, 36, 32180 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */ + 12700, 0x88, 0x00, 8, + 4, 2, 3, 20, 37, 20, 37, 6430 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */ + 24800, 0x89, 0x00, 9, + 6, 4, 3, 21, 38, 21, 38, 12860 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */ + 36600, 0x8a, 0x00, 10, + 6, 6, 3, 22, 39, 22, 39, 19300 }, + { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */ + 48100, 0x8b, 0x00, 11, + 8, 10, 3, 23, 40, 23, 40, 25736 }, + { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */ + 69500, 0x8c, 0x00, 12, + 8, 14, 3, 24, 41, 24, 41, 38600 }, + { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */ + 89500, 0x8d, 0x00, 13, + 8, 20, 3, 25, 42, 25, 42, 51472 }, + { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */ + 98900, 0x8e, 0x00, 14, + 8, 23, 3, 26, 43, 26, 44, 57890 }, + { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */ + 108300, 0x8f, 0x00, 15, + 8, 25, 3, 27, 44, 27, 45, 64320 }, + { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */ + 13200, 0x80, 0x00, 0, + 8, 2, 3, 12, 28, 28, 28, 6684 }, + { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */ + 25900, 0x81, 0x00, 1, + 8, 4, 3, 13, 29, 29, 29, 13368 }, + { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */ + 38600, 0x82, 0x00, 2, + 8, 6, 3, 14, 30, 30, 30, 20052 }, + { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */ + 49800, 0x83, 0x00, 3, + 8, 10, 3, 15, 31, 31, 31, 26738 }, + { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */ + 72200, 0x84, 0x00, 4, + 8, 14, 3, 16, 32, 32, 32, 40104 }, + { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */ + 92900, 0x85, 0x00, 5, + 8, 20, 3, 17, 33, 33, 33, 53476 }, + { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */ + 102700, 0x86, 0x00, 6, + 8, 23, 3, 18, 34, 34, 34, 60156 }, + { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */ + 112000, 0x87, 0x00, 7, + 8, 23, 3, 19, 35, 36, 36, 66840 }, + { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */ + 122000, 0x87, 0x00, 7, + 8, 25, 3, 19, 35, 36, 36, 74200 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */ + 25800, 0x88, 0x00, 8, + 8, 2, 3, 20, 37, 37, 37, 13360 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */ + 49800, 0x89, 0x00, 9, + 8, 4, 3, 21, 38, 38, 38, 26720 }, + { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */ + 71900, 0x8a, 0x00, 10, + 8, 6, 3, 22, 39, 39, 39, 40080 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */ + 92500, 0x8b, 0x00, 11, + 8, 10, 3, 23, 40, 40, 40, 53440 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */ + 130300, 0x8c, 0x00, 12, + 8, 14, 3, 24, 41, 41, 41, 80160 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */ + 162800, 0x8d, 0x00, 13, + 8, 20, 3, 25, 42, 42, 42, 106880 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */ + 178200, 0x8e, 0x00, 14, + 8, 23, 3, 26, 43, 43, 43, 120240 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */ + 192100, 0x8f, 0x00, 15, + 8, 23, 3, 27, 44, 45, 45, 133600 }, + { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */ + 207000, 0x8f, 0x00, 15, + 8, 25, 3, 27, 44, 45, 45, 148400 }, + }, + 50, /* probe interval */ + 50, /* rssi reduce interval */ + WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ +}; + +static const struct ath_rate_table ar5416_11a_ratetable = { + 8, + { + { VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ + 5400, 0x0b, 0x00, (0x80|12), + 0, 2, 1, 0, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ + 7800, 0x0f, 0x00, 18, + 0, 3, 1, 1, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ + 10000, 0x0a, 0x00, (0x80|24), + 2, 4, 2, 2, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ + 13900, 0x0e, 0x00, 36, + 2, 6, 2, 3, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ + 17300, 0x09, 0x00, (0x80|48), + 4, 10, 3, 4, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ + 23000, 0x0d, 0x00, 72, + 4, 14, 3, 5, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ + 27400, 0x08, 0x00, 96, + 4, 19, 3, 6, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ + 29300, 0x0c, 0x00, 108, + 4, 23, 3, 7, 0 }, + }, + 50, /* probe interval */ + 50, /* rssi reduce interval */ + 0, /* Phy rates allowed initially */ +}; + +static const struct ath_rate_table ar5416_11g_ratetable = { + 12, + { + { VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ + 900, 0x1b, 0x00, 2, + 0, 0, 1, 0, 0 }, + { VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */ + 1900, 0x1a, 0x04, 4, + 1, 1, 1, 1, 0 }, + { VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */ + 4900, 0x19, 0x04, 11, + 2, 2, 2, 2, 0 }, + { VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */ + 8100, 0x18, 0x04, 22, + 3, 3, 2, 3, 0 }, + { INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ + 5400, 0x0b, 0x00, 12, + 4, 2, 1, 4, 0 }, + { INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ + 7800, 0x0f, 0x00, 18, + 4, 3, 1, 5, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ + 10000, 0x0a, 0x00, 24, + 6, 4, 1, 6, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ + 13900, 0x0e, 0x00, 36, + 6, 6, 2, 7, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ + 17300, 0x09, 0x00, 48, + 8, 10, 3, 8, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ + 23000, 0x0d, 0x00, 72, + 8, 14, 3, 9, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ + 27400, 0x08, 0x00, 96, + 8, 19, 3, 10, 0 }, + { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ + 29300, 0x0c, 0x00, 108, + 8, 23, 3, 11, 0 }, + }, + 50, /* probe interval */ + 50, /* rssi reduce interval */ + 0, /* Phy rates allowed initially */ +}; + +static const struct ath_rate_table ar5416_11b_ratetable = { + 4, + { + { VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ + 900, 0x1b, 0x00, (0x80|2), + 0, 0, 1, 0, 0 }, + { VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */ + 1800, 0x1a, 0x04, (0x80|4), + 1, 1, 1, 1, 0 }, + { VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */ + 4300, 0x19, 0x04, (0x80|11), + 1, 2, 2, 2, 0 }, + { VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */ + 7100, 0x18, 0x04, (0x80|22), + 1, 4, 100, 3, 0 }, + }, + 100, /* probe interval */ + 100, /* rssi reduce interval */ + 0, /* Phy rates allowed initially */ +}; + +static inline int8_t median(int8_t a, int8_t b, int8_t c) +{ + if (a >= b) { + if (b >= c) + return b; + else if (a > c) + return c; + else + return a; + } else { + if (a >= c) + return a; + else if (b >= c) + return c; + else + return b; + } +} + +static void ath_rc_sort_validrates(const struct ath_rate_table *rate_table, + struct ath_rate_priv *ath_rc_priv) +{ + u8 i, j, idx, idx_next; + + for (i = ath_rc_priv->max_valid_rate - 1; i > 0; i--) { + for (j = 0; j <= i-1; j++) { + idx = ath_rc_priv->valid_rate_index[j]; + idx_next = ath_rc_priv->valid_rate_index[j+1]; + + if (rate_table->info[idx].ratekbps > + rate_table->info[idx_next].ratekbps) { + ath_rc_priv->valid_rate_index[j] = idx_next; + ath_rc_priv->valid_rate_index[j+1] = idx; + } + } + } +} + +static void ath_rc_init_valid_txmask(struct ath_rate_priv *ath_rc_priv) +{ + u8 i; + + for (i = 0; i < ath_rc_priv->rate_table_size; i++) + ath_rc_priv->valid_rate_index[i] = 0; +} + +static inline void ath_rc_set_valid_txmask(struct ath_rate_priv *ath_rc_priv, + u8 index, int valid_tx_rate) +{ + ASSERT(index <= ath_rc_priv->rate_table_size); + ath_rc_priv->valid_rate_index[index] = valid_tx_rate ? 1 : 0; +} + +static inline int ath_rc_isvalid_txmask(struct ath_rate_priv *ath_rc_priv, + u8 index) +{ + ASSERT(index <= ath_rc_priv->rate_table_size); + return ath_rc_priv->valid_rate_index[index]; +} + +static inline +int ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table, + struct ath_rate_priv *ath_rc_priv, + u8 cur_valid_txrate, + u8 *next_idx) +{ + u8 i; + + for (i = 0; i < ath_rc_priv->max_valid_rate - 1; i++) { + if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) { + *next_idx = ath_rc_priv->valid_rate_index[i+1]; + return 1; + } + } + + /* No more valid rates */ + *next_idx = 0; + + return 0; +} + +/* Return true only for single stream */ + +static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw) +{ + if (WLAN_RC_PHY_HT(phy) && !(capflag & WLAN_RC_HT_FLAG)) + return 0; + if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG)) + return 0; + if (WLAN_RC_PHY_SGI(phy) && !(capflag & WLAN_RC_SGI_FLAG)) + return 0; + if (!ignore_cw && WLAN_RC_PHY_HT(phy)) + if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG)) + return 0; + if (!WLAN_RC_PHY_40(phy) && (capflag & WLAN_RC_40_FLAG)) + return 0; + return 1; +} + +static inline int +ath_rc_get_nextlowervalid_txrate(const struct ath_rate_table *rate_table, + struct ath_rate_priv *ath_rc_priv, + u8 cur_valid_txrate, u8 *next_idx) +{ + int8_t i; + + for (i = 1; i < ath_rc_priv->max_valid_rate ; i++) { + if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) { + *next_idx = ath_rc_priv->valid_rate_index[i-1]; + return 1; + } + } + + return 0; +} + +static u8 ath_rc_init_validrates(struct ath_rate_priv *ath_rc_priv, + const struct ath_rate_table *rate_table, + u32 capflag) +{ + u8 i, hi = 0; + u32 valid; + + for (i = 0; i < rate_table->rate_cnt; i++) { + valid = (!(ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG) ? + rate_table->info[i].valid_single_stream : + rate_table->info[i].valid); + if (valid == 1) { + u32 phy = rate_table->info[i].phy; + u8 valid_rate_count = 0; + + if (!ath_rc_valid_phyrate(phy, capflag, 0)) + continue; + + valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy]; + + ath_rc_priv->valid_phy_rateidx[phy][valid_rate_count] = i; + ath_rc_priv->valid_phy_ratecnt[phy] += 1; + ath_rc_set_valid_txmask(ath_rc_priv, i, 1); + hi = A_MAX(hi, i); + } + } + + return hi; +} + +static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, + const struct ath_rate_table *rate_table, + struct ath_rateset *rateset, + u32 capflag) +{ + u8 i, j, hi = 0; + + /* Use intersection of working rates and valid rates */ + for (i = 0; i < rateset->rs_nrates; i++) { + for (j = 0; j < rate_table->rate_cnt; j++) { + u32 phy = rate_table->info[j].phy; + u32 valid = (!(ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG) ? + rate_table->info[j].valid_single_stream : + rate_table->info[j].valid); + u8 rate = rateset->rs_rates[i]; + u8 dot11rate = rate_table->info[j].dot11rate; + + /* We allow a rate only if its valid and the + * capflag matches one of the validity + * (VALID/VALID_20/VALID_40) flags */ + + if (((rate & 0x7F) == (dot11rate & 0x7F)) && + ((valid & WLAN_RC_CAP_MODE(capflag)) == + WLAN_RC_CAP_MODE(capflag)) && + !WLAN_RC_PHY_HT(phy)) { + u8 valid_rate_count = 0; + + if (!ath_rc_valid_phyrate(phy, capflag, 0)) + continue; + + valid_rate_count = + ath_rc_priv->valid_phy_ratecnt[phy]; + + ath_rc_priv->valid_phy_rateidx[phy] + [valid_rate_count] = j; + ath_rc_priv->valid_phy_ratecnt[phy] += 1; + ath_rc_set_valid_txmask(ath_rc_priv, j, 1); + hi = A_MAX(hi, j); + } + } + } + + return hi; +} + +static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv, + const struct ath_rate_table *rate_table, + u8 *mcs_set, u32 capflag) +{ + struct ath_rateset *rateset = (struct ath_rateset *)mcs_set; + + u8 i, j, hi = 0; + + /* Use intersection of working rates and valid rates */ + for (i = 0; i < rateset->rs_nrates; i++) { + for (j = 0; j < rate_table->rate_cnt; j++) { + u32 phy = rate_table->info[j].phy; + u32 valid = (!(ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG) ? + rate_table->info[j].valid_single_stream : + rate_table->info[j].valid); + u8 rate = rateset->rs_rates[i]; + u8 dot11rate = rate_table->info[j].dot11rate; + + if (((rate & 0x7F) != (dot11rate & 0x7F)) || + !WLAN_RC_PHY_HT(phy) || + !WLAN_RC_PHY_HT_VALID(valid, capflag)) + continue; + + if (!ath_rc_valid_phyrate(phy, capflag, 0)) + continue; + + ath_rc_priv->valid_phy_rateidx[phy] + [ath_rc_priv->valid_phy_ratecnt[phy]] = j; + ath_rc_priv->valid_phy_ratecnt[phy] += 1; + ath_rc_set_valid_txmask(ath_rc_priv, j, 1); + hi = A_MAX(hi, j); + } + } + + return hi; +} + +static u8 ath_rc_ratefind_ht(struct ath_softc *sc, + struct ath_rate_priv *ath_rc_priv, + const struct ath_rate_table *rate_table, + int *is_probing) +{ + u32 dt, best_thruput, this_thruput, now_msec; + u8 rate, next_rate, best_rate, maxindex, minindex; + int8_t rssi_last, rssi_reduce = 0, index = 0; + + *is_probing = 0; + + rssi_last = median(ath_rc_priv->rssi_last, + ath_rc_priv->rssi_last_prev, + ath_rc_priv->rssi_last_prev2); + + /* + * Age (reduce) last ack rssi based on how old it is. + * The bizarre numbers are so the delta is 160msec, + * meaning we divide by 16. + * 0msec <= dt <= 25msec: don't derate + * 25msec <= dt <= 185msec: derate linearly from 0 to 10dB + * 185msec <= dt: derate by 10dB + */ + + now_msec = jiffies_to_msecs(jiffies); + dt = now_msec - ath_rc_priv->rssi_time; + + if (dt >= 185) + rssi_reduce = 10; + else if (dt >= 25) + rssi_reduce = (u8)((dt - 25) >> 4); + + /* Now reduce rssi_last by rssi_reduce */ + if (rssi_last < rssi_reduce) + rssi_last = 0; + else + rssi_last -= rssi_reduce; + + /* + * Now look up the rate in the rssi table and return it. + * If no rates match then we return 0 (lowest rate) + */ + + best_thruput = 0; + maxindex = ath_rc_priv->max_valid_rate-1; + + minindex = 0; + best_rate = minindex; + + /* + * Try the higher rate first. It will reduce memory moving time + * if we have very good channel characteristics. + */ + for (index = maxindex; index >= minindex ; index--) { + u8 per_thres; + + rate = ath_rc_priv->valid_rate_index[index]; + if (rate > ath_rc_priv->rate_max_phy) + continue; + + /* + * For TCP the average collision rate is around 11%, + * so we ignore PERs less than this. This is to + * prevent the rate we are currently using (whose + * PER might be in the 10-15 range because of TCP + * collisions) looking worse than the next lower + * rate whose PER has decayed close to 0. If we + * used to next lower rate, its PER would grow to + * 10-15 and we would be worse off then staying + * at the current rate. + */ + per_thres = ath_rc_priv->state[rate].per; + if (per_thres < 12) + per_thres = 12; + + this_thruput = rate_table->info[rate].user_ratekbps * + (100 - per_thres); + + if (best_thruput <= this_thruput) { + best_thruput = this_thruput; + best_rate = rate; + } + } + + rate = best_rate; + ath_rc_priv->rssi_last_lookup = rssi_last; + + /* + * Must check the actual rate (ratekbps) to account for + * non-monoticity of 11g's rate table + */ + + if (rate >= ath_rc_priv->rate_max_phy) { + rate = ath_rc_priv->rate_max_phy; + + /* Probe the next allowed phy state */ + if (ath_rc_get_nextvalid_txrate(rate_table, + ath_rc_priv, rate, &next_rate) && + (now_msec - ath_rc_priv->probe_time > + rate_table->probe_interval) && + (ath_rc_priv->hw_maxretry_pktcnt >= 1)) { + rate = next_rate; + ath_rc_priv->probe_rate = rate; + ath_rc_priv->probe_time = now_msec; + ath_rc_priv->hw_maxretry_pktcnt = 0; + *is_probing = 1; + } + } + + if (rate > (ath_rc_priv->rate_table_size - 1)) + rate = ath_rc_priv->rate_table_size - 1; + + ASSERT((rate_table->info[rate].valid && + (ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG)) || + (rate_table->info[rate].valid_single_stream && + !(ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG))); + + return rate; +} + +static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table, + struct ieee80211_tx_rate *rate, + struct ieee80211_tx_rate_control *txrc, + u8 tries, u8 rix, int rtsctsenable) +{ + rate->count = tries; + rate->idx = rix; + + if (txrc->short_preamble) + rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; + if (txrc->rts || rtsctsenable) + rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; + if (WLAN_RC_PHY_40(rate_table->info[rix].phy)) + rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy)) + rate->flags |= IEEE80211_TX_RC_SHORT_GI; + if (WLAN_RC_PHY_HT(rate_table->info[rix].phy)) + rate->flags |= IEEE80211_TX_RC_MCS; +} + +static void ath_rc_rate_set_rtscts(struct ath_softc *sc, + const struct ath_rate_table *rate_table, + struct ieee80211_tx_info *tx_info) +{ + struct ieee80211_tx_rate *rates = tx_info->control.rates; + int i = 0, rix = 0, cix, enable_g_protection = 0; + + /* get the cix for the lowest valid rix */ + for (i = 3; i >= 0; i--) { + if (rates[i].count && (rates[i].idx >= 0)) { + rix = rates[i].idx; + break; + } + } + cix = rate_table->info[rix].ctrl_rate; + + /* All protection frames are transmited at 2Mb/s for 802.11g, + * otherwise we transmit them at 1Mb/s */ + if (sc->hw->conf.channel->band == IEEE80211_BAND_2GHZ && + !conf_is_ht(&sc->hw->conf)) + enable_g_protection = 1; + + /* + * If 802.11g protection is enabled, determine whether to use RTS/CTS or + * just CTS. Note that this is only done for OFDM/HT unicast frames. + */ + if ((sc->sc_flags & SC_OP_PROTECT_ENABLE) && + !(tx_info->flags & IEEE80211_TX_CTL_NO_ACK) && + (rate_table->info[rix].phy == WLAN_RC_PHY_OFDM || + WLAN_RC_PHY_HT(rate_table->info[rix].phy))) { + rates[0].flags |= IEEE80211_TX_RC_USE_CTS_PROTECT; + cix = rate_table->info[enable_g_protection].ctrl_rate; + } + + tx_info->control.rts_cts_rate_idx = cix; +} + +static u8 ath_rc_rate_getidx(struct ath_softc *sc, + struct ath_rate_priv *ath_rc_priv, + const struct ath_rate_table *rate_table, + u8 rix, u16 stepdown, + u16 min_rate) +{ + u32 j; + u8 nextindex = 0; + + if (min_rate) { + for (j = RATE_TABLE_SIZE; j > 0; j--) { + if (ath_rc_get_nextlowervalid_txrate(rate_table, + ath_rc_priv, rix, &nextindex)) + rix = nextindex; + else + break; + } + } else { + for (j = stepdown; j > 0; j--) { + if (ath_rc_get_nextlowervalid_txrate(rate_table, + ath_rc_priv, rix, &nextindex)) + rix = nextindex; + else + break; + } + } + return rix; +} + +static void ath_rc_ratefind(struct ath_softc *sc, + struct ath_rate_priv *ath_rc_priv, + struct ieee80211_tx_rate_control *txrc) +{ + const struct ath_rate_table *rate_table; + struct sk_buff *skb = txrc->skb; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *rates = tx_info->control.rates; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + __le16 fc = hdr->frame_control; + u8 try_per_rate = 0, i = 0, rix, nrix; + int is_probe = 0; + + rate_table = sc->cur_rate_table; + rix = ath_rc_ratefind_ht(sc, ath_rc_priv, rate_table, &is_probe); + nrix = rix; + + if (is_probe) { + /* set one try for probe rates. For the + * probes don't enable rts */ + ath_rc_rate_set_series(rate_table, &rates[i++], txrc, + 1, nrix, 0); + + try_per_rate = (ATH_11N_TXMAXTRY/4); + /* Get the next tried/allowed rate. No RTS for the next series + * after the probe rate + */ + nrix = ath_rc_rate_getidx(sc, ath_rc_priv, + rate_table, nrix, 1, 0); + ath_rc_rate_set_series(rate_table, &rates[i++], txrc, + try_per_rate, nrix, 0); + + tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; + } else { + try_per_rate = (ATH_11N_TXMAXTRY/4); + /* Set the choosen rate. No RTS for first series entry. */ + ath_rc_rate_set_series(rate_table, &rates[i++], txrc, + try_per_rate, nrix, 0); + } + + /* Fill in the other rates for multirate retry */ + for ( ; i < 4; i++) { + u8 try_num; + u8 min_rate; + + try_num = ((i + 1) == 4) ? + ATH_11N_TXMAXTRY - (try_per_rate * i) : try_per_rate ; + min_rate = (((i + 1) == 4) && 0); + + nrix = ath_rc_rate_getidx(sc, ath_rc_priv, + rate_table, nrix, 1, min_rate); + /* All other rates in the series have RTS enabled */ + ath_rc_rate_set_series(rate_table, &rates[i], txrc, + try_num, nrix, 1); + } + + /* + * NB:Change rate series to enable aggregation when operating + * at lower MCS rates. When first rate in series is MCS2 + * in HT40 @ 2.4GHz, series should look like: + * + * {MCS2, MCS1, MCS0, MCS0}. + * + * When first rate in series is MCS3 in HT20 @ 2.4GHz, series should + * look like: + * + * {MCS3, MCS2, MCS1, MCS1} + * + * So, set fourth rate in series to be same as third one for + * above conditions. + */ + if ((sc->hw->conf.channel->band == IEEE80211_BAND_2GHZ) && + (conf_is_ht(&sc->hw->conf))) { + u8 dot11rate = rate_table->info[rix].dot11rate; + u8 phy = rate_table->info[rix].phy; + if (i == 4 && + ((dot11rate == 2 && phy == WLAN_RC_PHY_HT_40_SS) || + (dot11rate == 3 && phy == WLAN_RC_PHY_HT_20_SS))) { + rates[3].idx = rates[2].idx; + rates[3].flags = rates[2].flags; + } + } + + /* + * Force hardware to use computed duration for next + * fragment by disabling multi-rate retry, which + * updates duration based on the multi-rate duration table. + * + * FIXME: Fix duration + */ + if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK) && + (ieee80211_has_morefrags(fc) || + (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG))) { + rates[1].count = rates[2].count = rates[3].count = 0; + rates[1].idx = rates[2].idx = rates[3].idx = 0; + rates[0].count = ATH_TXMAXTRY; + } + + /* Setup RTS/CTS */ + ath_rc_rate_set_rtscts(sc, rate_table, tx_info); +} + +static bool ath_rc_update_per(struct ath_softc *sc, + const struct ath_rate_table *rate_table, + struct ath_rate_priv *ath_rc_priv, + struct ath_tx_info_priv *tx_info_priv, + int tx_rate, int xretries, int retries, + u32 now_msec) +{ + bool state_change = false; + int count; + u8 last_per; + static u32 nretry_to_per_lookup[10] = { + 100 * 0 / 1, + 100 * 1 / 4, + 100 * 1 / 2, + 100 * 3 / 4, + 100 * 4 / 5, + 100 * 5 / 6, + 100 * 6 / 7, + 100 * 7 / 8, + 100 * 8 / 9, + 100 * 9 / 10 + }; + + last_per = ath_rc_priv->state[tx_rate].per; + + if (xretries) { + if (xretries == 1) { + ath_rc_priv->state[tx_rate].per += 30; + if (ath_rc_priv->state[tx_rate].per > 100) + ath_rc_priv->state[tx_rate].per = 100; + } else { + /* xretries == 2 */ + count = ARRAY_SIZE(nretry_to_per_lookup); + if (retries >= count) + retries = count - 1; + + /* new_PER = 7/8*old_PER + 1/8*(currentPER) */ + ath_rc_priv->state[tx_rate].per = + (u8)(last_per - (last_per >> 3) + (100 >> 3)); + } + + /* xretries == 1 or 2 */ + + if (ath_rc_priv->probe_rate == tx_rate) + ath_rc_priv->probe_rate = 0; + + } else { /* xretries == 0 */ + count = ARRAY_SIZE(nretry_to_per_lookup); + if (retries >= count) + retries = count - 1; + + if (tx_info_priv->n_bad_frames) { + /* new_PER = 7/8*old_PER + 1/8*(currentPER) + * Assuming that n_frames is not 0. The current PER + * from the retries is 100 * retries / (retries+1), + * since the first retries attempts failed, and the + * next one worked. For the one that worked, + * n_bad_frames subframes out of n_frames wored, + * so the PER for that part is + * 100 * n_bad_frames / n_frames, and it contributes + * 100 * n_bad_frames / (n_frames * (retries+1)) to + * the above PER. The expression below is a + * simplified version of the sum of these two terms. + */ + if (tx_info_priv->n_frames > 0) { + int n_frames, n_bad_frames; + u8 cur_per, new_per; + + n_bad_frames = retries * tx_info_priv->n_frames + + tx_info_priv->n_bad_frames; + n_frames = tx_info_priv->n_frames * (retries + 1); + cur_per = (100 * n_bad_frames / n_frames) >> 3; + new_per = (u8)(last_per - (last_per >> 3) + cur_per); + ath_rc_priv->state[tx_rate].per = new_per; + } + } else { + ath_rc_priv->state[tx_rate].per = + (u8)(last_per - (last_per >> 3) + + (nretry_to_per_lookup[retries] >> 3)); + } + + ath_rc_priv->rssi_last_prev2 = ath_rc_priv->rssi_last_prev; + ath_rc_priv->rssi_last_prev = ath_rc_priv->rssi_last; + ath_rc_priv->rssi_last = tx_info_priv->tx.ts_rssi; + ath_rc_priv->rssi_time = now_msec; + + /* + * If we got at most one retry then increase the max rate if + * this was a probe. Otherwise, ignore the probe. + */ + if (ath_rc_priv->probe_rate && ath_rc_priv->probe_rate == tx_rate) { + if (retries > 0 || 2 * tx_info_priv->n_bad_frames > + tx_info_priv->n_frames) { + /* + * Since we probed with just a single attempt, + * any retries means the probe failed. Also, + * if the attempt worked, but more than half + * the subframes were bad then also consider + * the probe a failure. + */ + ath_rc_priv->probe_rate = 0; + } else { + u8 probe_rate = 0; + + ath_rc_priv->rate_max_phy = + ath_rc_priv->probe_rate; + probe_rate = ath_rc_priv->probe_rate; + + if (ath_rc_priv->state[probe_rate].per > 30) + ath_rc_priv->state[probe_rate].per = 20; + + ath_rc_priv->probe_rate = 0; + + /* + * Since this probe succeeded, we allow the next + * probe twice as soon. This allows the maxRate + * to move up faster if the probes are + * succesful. + */ + ath_rc_priv->probe_time = + now_msec - rate_table->probe_interval / 2; + } + } + + if (retries > 0) { + /* + * Don't update anything. We don't know if + * this was because of collisions or poor signal. + * + * Later: if rssi_ack is close to + * ath_rc_priv->state[txRate].rssi_thres and we see lots + * of retries, then we could increase + * ath_rc_priv->state[txRate].rssi_thres. + */ + ath_rc_priv->hw_maxretry_pktcnt = 0; + } else { + int32_t rssi_ackAvg; + int8_t rssi_thres; + int8_t rssi_ack_vmin; + + /* + * It worked with no retries. First ignore bogus (small) + * rssi_ack values. + */ + if (tx_rate == ath_rc_priv->rate_max_phy && + ath_rc_priv->hw_maxretry_pktcnt < 255) { + ath_rc_priv->hw_maxretry_pktcnt++; + } + + if (tx_info_priv->tx.ts_rssi < + rate_table->info[tx_rate].rssi_ack_validmin) + goto exit; + + /* Average the rssi */ + if (tx_rate != ath_rc_priv->rssi_sum_rate) { + ath_rc_priv->rssi_sum_rate = tx_rate; + ath_rc_priv->rssi_sum = + ath_rc_priv->rssi_sum_cnt = 0; + } + + ath_rc_priv->rssi_sum += tx_info_priv->tx.ts_rssi; + ath_rc_priv->rssi_sum_cnt++; + + if (ath_rc_priv->rssi_sum_cnt < 4) + goto exit; + + rssi_ackAvg = + (ath_rc_priv->rssi_sum + 2) / 4; + rssi_thres = + ath_rc_priv->state[tx_rate].rssi_thres; + rssi_ack_vmin = + rate_table->info[tx_rate].rssi_ack_validmin; + + ath_rc_priv->rssi_sum = + ath_rc_priv->rssi_sum_cnt = 0; + + /* Now reduce the current rssi threshold */ + if ((rssi_ackAvg < rssi_thres + 2) && + (rssi_thres > rssi_ack_vmin)) { + ath_rc_priv->state[tx_rate].rssi_thres--; + } + + state_change = true; + } + } +exit: + return state_change; +} + +/* Update PER, RSSI and whatever else that the code thinks it is doing. + If you can make sense of all this, you really need to go out more. */ + +static void ath_rc_update_ht(struct ath_softc *sc, + struct ath_rate_priv *ath_rc_priv, + struct ath_tx_info_priv *tx_info_priv, + int tx_rate, int xretries, int retries) +{ +#define CHK_RSSI(rate) \ + ((ath_rc_priv->state[(rate)].rssi_thres + \ + rate_table->info[(rate)].rssi_ack_deltamin) > \ + ath_rc_priv->state[(rate)+1].rssi_thres) + + u32 now_msec = jiffies_to_msecs(jiffies); + int rate; + u8 last_per; + bool state_change = false; + const struct ath_rate_table *rate_table = sc->cur_rate_table; + int size = ath_rc_priv->rate_table_size; + + if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt)) + return; + + /* To compensate for some imbalance between ctrl and ext. channel */ + + if (WLAN_RC_PHY_40(rate_table->info[tx_rate].phy)) + tx_info_priv->tx.ts_rssi = + tx_info_priv->tx.ts_rssi < 3 ? 0 : + tx_info_priv->tx.ts_rssi - 3; + + last_per = ath_rc_priv->state[tx_rate].per; + + /* Update PER first */ + state_change = ath_rc_update_per(sc, rate_table, ath_rc_priv, + tx_info_priv, tx_rate, xretries, + retries, now_msec); + + /* + * If this rate looks bad (high PER) then stop using it for + * a while (except if we are probing). + */ + if (ath_rc_priv->state[tx_rate].per >= 55 && tx_rate > 0 && + rate_table->info[tx_rate].ratekbps <= + rate_table->info[ath_rc_priv->rate_max_phy].ratekbps) { + ath_rc_get_nextlowervalid_txrate(rate_table, ath_rc_priv, + (u8)tx_rate, &ath_rc_priv->rate_max_phy); + + /* Don't probe for a little while. */ + ath_rc_priv->probe_time = now_msec; + } + + if (state_change) { + /* + * Make sure the rates above this have higher rssi thresholds. + * (Note: Monotonicity is kept within the OFDM rates and + * within the CCK rates. However, no adjustment is + * made to keep the rssi thresholds monotonically + * increasing between the CCK and OFDM rates.) + */ + for (rate = tx_rate; rate < size - 1; rate++) { + if (rate_table->info[rate+1].phy != + rate_table->info[tx_rate].phy) + break; + + if (CHK_RSSI(rate)) { + ath_rc_priv->state[rate+1].rssi_thres = + ath_rc_priv->state[rate].rssi_thres + + rate_table->info[rate].rssi_ack_deltamin; + } + } + + /* Make sure the rates below this have lower rssi thresholds. */ + for (rate = tx_rate - 1; rate >= 0; rate--) { + if (rate_table->info[rate].phy != + rate_table->info[tx_rate].phy) + break; + + if (CHK_RSSI(rate)) { + if (ath_rc_priv->state[rate+1].rssi_thres < + rate_table->info[rate].rssi_ack_deltamin) + ath_rc_priv->state[rate].rssi_thres = 0; + else { + ath_rc_priv->state[rate].rssi_thres = + ath_rc_priv->state[rate+1].rssi_thres - + rate_table->info[rate].rssi_ack_deltamin; + } + + if (ath_rc_priv->state[rate].rssi_thres < + rate_table->info[rate].rssi_ack_validmin) { + ath_rc_priv->state[rate].rssi_thres = + rate_table->info[rate].rssi_ack_validmin; + } + } + } + } + + /* Make sure the rates below this have lower PER */ + /* Monotonicity is kept only for rates below the current rate. */ + if (ath_rc_priv->state[tx_rate].per < last_per) { + for (rate = tx_rate - 1; rate >= 0; rate--) { + if (rate_table->info[rate].phy != + rate_table->info[tx_rate].phy) + break; + + if (ath_rc_priv->state[rate].per > + ath_rc_priv->state[rate+1].per) { + ath_rc_priv->state[rate].per = + ath_rc_priv->state[rate+1].per; + } + } + } + + /* Maintain monotonicity for rates above the current rate */ + for (rate = tx_rate; rate < size - 1; rate++) { + if (ath_rc_priv->state[rate+1].per < + ath_rc_priv->state[rate].per) + ath_rc_priv->state[rate+1].per = + ath_rc_priv->state[rate].per; + } + + /* Every so often, we reduce the thresholds and + * PER (different for CCK and OFDM). */ + if (now_msec - ath_rc_priv->rssi_down_time >= + rate_table->rssi_reduce_interval) { + + for (rate = 0; rate < size; rate++) { + if (ath_rc_priv->state[rate].rssi_thres > + rate_table->info[rate].rssi_ack_validmin) + ath_rc_priv->state[rate].rssi_thres -= 1; + } + ath_rc_priv->rssi_down_time = now_msec; + } + + /* Every so often, we reduce the thresholds + * and PER (different for CCK and OFDM). */ + if (now_msec - ath_rc_priv->per_down_time >= + rate_table->rssi_reduce_interval) { + for (rate = 0; rate < size; rate++) { + ath_rc_priv->state[rate].per = + 7 * ath_rc_priv->state[rate].per / 8; + } + + ath_rc_priv->per_down_time = now_msec; + } + + ath_debug_stat_retries(sc, tx_rate, xretries, retries, + ath_rc_priv->state[tx_rate].per); + +#undef CHK_RSSI +} + +static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table, + struct ieee80211_tx_rate *rate) +{ + int rix; + + if ((rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) && + (rate->flags & IEEE80211_TX_RC_SHORT_GI)) + rix = rate_table->info[rate->idx].ht_index; + else if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + rix = rate_table->info[rate->idx].sgi_index; + else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + rix = rate_table->info[rate->idx].cw40index; + else + rix = rate_table->info[rate->idx].base_index; + + return rix; +} + +static void ath_rc_tx_status(struct ath_softc *sc, + struct ath_rate_priv *ath_rc_priv, + struct ieee80211_tx_info *tx_info, + int final_ts_idx, int xretries, int long_retry) +{ + struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); + const struct ath_rate_table *rate_table; + struct ieee80211_tx_rate *rates = tx_info->status.rates; + u8 flags; + u32 i = 0, rix; + + rate_table = sc->cur_rate_table; + + /* + * If the first rate is not the final index, there + * are intermediate rate failures to be processed. + */ + if (final_ts_idx != 0) { + /* Process intermediate rates that failed.*/ + for (i = 0; i < final_ts_idx ; i++) { + if (rates[i].count != 0 && (rates[i].idx >= 0)) { + flags = rates[i].flags; + + /* If HT40 and we have switched mode from + * 40 to 20 => don't update */ + + if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) && + !(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG)) + return; + + rix = ath_rc_get_rateindex(rate_table, &rates[i]); + ath_rc_update_ht(sc, ath_rc_priv, + tx_info_priv, rix, + xretries ? 1 : 2, + rates[i].count); + } + } + } else { + /* + * Handle the special case of MIMO PS burst, where the second + * aggregate is sent out with only one rate and one try. + * Treating it as an excessive retry penalizes the rate + * inordinately. + */ + if (rates[0].count == 1 && xretries == 1) + xretries = 2; + } + + flags = rates[i].flags; + + /* If HT40 and we have switched mode from 40 to 20 => don't update */ + if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) && + !(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG)) + return; + + rix = ath_rc_get_rateindex(rate_table, &rates[i]); + ath_rc_update_ht(sc, ath_rc_priv, tx_info_priv, rix, + xretries, long_retry); +} + +static const +struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, + enum ieee80211_band band, + bool is_ht, + bool is_cw_40) +{ + int mode = 0; + + switch(band) { + case IEEE80211_BAND_2GHZ: + mode = ATH9K_MODE_11G; + if (is_ht) + mode = ATH9K_MODE_11NG_HT20; + if (is_cw_40) + mode = ATH9K_MODE_11NG_HT40PLUS; + break; + case IEEE80211_BAND_5GHZ: + mode = ATH9K_MODE_11A; + if (is_ht) + mode = ATH9K_MODE_11NA_HT20; + if (is_cw_40) + mode = ATH9K_MODE_11NA_HT40PLUS; + break; + default: + DPRINTF(sc, ATH_DBG_CONFIG, "Invalid band\n"); + return NULL; + } + + BUG_ON(mode >= ATH9K_MODE_MAX); + + DPRINTF(sc, ATH_DBG_CONFIG, "Choosing rate table for mode: %d\n", mode); + return sc->hw_rate_table[mode]; +} + +static void ath_rc_init(struct ath_softc *sc, + struct ath_rate_priv *ath_rc_priv, + struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, + const struct ath_rate_table *rate_table) +{ + struct ath_rateset *rateset = &ath_rc_priv->neg_rates; + u8 *ht_mcs = (u8 *)&ath_rc_priv->neg_ht_rates; + u8 i, j, k, hi = 0, hthi = 0; + + if (!rate_table) { + DPRINTF(sc, ATH_DBG_FATAL, "Rate table not initialized\n"); + return; + } + + /* Initial rate table size. Will change depending + * on the working rate set */ + ath_rc_priv->rate_table_size = RATE_TABLE_SIZE; + + /* Initialize thresholds according to the global rate table */ + for (i = 0 ; i < ath_rc_priv->rate_table_size; i++) { + ath_rc_priv->state[i].rssi_thres = + rate_table->info[i].rssi_ack_validmin; + ath_rc_priv->state[i].per = 0; + } + + /* Determine the valid rates */ + ath_rc_init_valid_txmask(ath_rc_priv); + + for (i = 0; i < WLAN_RC_PHY_MAX; i++) { + for (j = 0; j < MAX_TX_RATE_PHY; j++) + ath_rc_priv->valid_phy_rateidx[i][j] = 0; + ath_rc_priv->valid_phy_ratecnt[i] = 0; + } + + if (!rateset->rs_nrates) { + /* No working rate, just initialize valid rates */ + hi = ath_rc_init_validrates(ath_rc_priv, rate_table, + ath_rc_priv->ht_cap); + } else { + /* Use intersection of working rates and valid rates */ + hi = ath_rc_setvalid_rates(ath_rc_priv, rate_table, + rateset, ath_rc_priv->ht_cap); + if (ath_rc_priv->ht_cap & WLAN_RC_HT_FLAG) { + hthi = ath_rc_setvalid_htrates(ath_rc_priv, + rate_table, + ht_mcs, + ath_rc_priv->ht_cap); + } + hi = A_MAX(hi, hthi); + } + + ath_rc_priv->rate_table_size = hi + 1; + ath_rc_priv->rate_max_phy = 0; + ASSERT(ath_rc_priv->rate_table_size <= RATE_TABLE_SIZE); + + for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) { + for (j = 0; j < ath_rc_priv->valid_phy_ratecnt[i]; j++) { + ath_rc_priv->valid_rate_index[k++] = + ath_rc_priv->valid_phy_rateidx[i][j]; + } + + if (!ath_rc_valid_phyrate(i, rate_table->initial_ratemax, 1) + || !ath_rc_priv->valid_phy_ratecnt[i]) + continue; + + ath_rc_priv->rate_max_phy = ath_rc_priv->valid_phy_rateidx[i][j-1]; + } + ASSERT(ath_rc_priv->rate_table_size <= RATE_TABLE_SIZE); + ASSERT(k <= RATE_TABLE_SIZE); + + ath_rc_priv->max_valid_rate = k; + ath_rc_sort_validrates(rate_table, ath_rc_priv); + ath_rc_priv->rate_max_phy = ath_rc_priv->valid_rate_index[k-4]; + sc->cur_rate_table = rate_table; + + DPRINTF(sc, ATH_DBG_CONFIG, "RC Initialized with capabilities: 0x%x\n", + ath_rc_priv->ht_cap); +} + +static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta, + bool is_cw40, bool is_sgi40) +{ + u8 caps = 0; + + if (sta->ht_cap.ht_supported) { + caps = WLAN_RC_HT_FLAG; + if (sc->sc_ah->caps.tx_chainmask != 1 && + ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_DS, 0, NULL)) { + if (sta->ht_cap.mcs.rx_mask[1]) + caps |= WLAN_RC_DS_FLAG; + } + if (is_cw40) + caps |= WLAN_RC_40_FLAG; + if (is_sgi40) + caps |= WLAN_RC_SGI_FLAG; + } + + return caps; +} + +/***********************************/ +/* mac80211 Rate Control callbacks */ +/***********************************/ + +static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta, + struct sk_buff *skb) +{ + struct ath_softc *sc = priv; + struct ath_rate_priv *ath_rc_priv = priv_sta; + struct ath_tx_info_priv *tx_info_priv = NULL; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr; + int final_ts_idx, tx_status = 0, is_underrun = 0; + __le16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + tx_info_priv = ATH_TX_INFO_PRIV(tx_info); + final_ts_idx = tx_info_priv->tx.ts_rateindex; + + if (!priv_sta || !ieee80211_is_data(fc) || + !tx_info_priv->update_rc) + goto exit; + + if (tx_info_priv->tx.ts_status & ATH9K_TXERR_FILT) + goto exit; + + /* + * If underrun error is seen assume it as an excessive retry only + * if prefetch trigger level have reached the max (0x3f for 5416) + * Adjust the long retry as if the frame was tried ATH_11N_TXMAXTRY + * times. This affects how ratectrl updates PER for the failed rate. + */ + if (tx_info_priv->tx.ts_flags & + (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN) && + ((sc->sc_ah->tx_trig_level) >= ath_rc_priv->tx_triglevel_max)) { + tx_status = 1; + is_underrun = 1; + } + + if ((tx_info_priv->tx.ts_status & ATH9K_TXERR_XRETRY) || + (tx_info_priv->tx.ts_status & ATH9K_TXERR_FIFO)) + tx_status = 1; + + ath_rc_tx_status(sc, ath_rc_priv, tx_info, final_ts_idx, tx_status, + (is_underrun) ? ATH_11N_TXMAXTRY : + tx_info_priv->tx.ts_longretry); + + /* Check if aggregation has to be enabled for this tid */ + if (conf_is_ht(&sc->hw->conf) && + !(skb->protocol == cpu_to_be16(ETH_P_PAE))) { + if (ieee80211_is_data_qos(fc)) { + u8 *qc, tid; + struct ath_node *an; + + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + an = (struct ath_node *)sta->drv_priv; + + if(ath_tx_aggr_check(sc, an, tid)) + ieee80211_start_tx_ba_session(sc->hw, hdr->addr1, tid); + } + } + + ath_debug_stat_rc(sc, skb); +exit: + kfree(tx_info_priv); +} + +static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, + struct ieee80211_tx_rate_control *txrc) +{ + struct ieee80211_supported_band *sband = txrc->sband; + struct sk_buff *skb = txrc->skb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ath_softc *sc = priv; + struct ath_rate_priv *ath_rc_priv = priv_sta; + __le16 fc = hdr->frame_control; + + /* lowest rate for management and NO_ACK frames */ + if (!ieee80211_is_data(fc) || + tx_info->flags & IEEE80211_TX_CTL_NO_ACK || !sta) { + tx_info->control.rates[0].idx = rate_lowest_index(sband, sta); + tx_info->control.rates[0].count = + (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) ? + 1 : ATH_MGT_TXMAXTRY; + return; + } + + /* Find tx rate for unicast frames */ + ath_rc_ratefind(sc, ath_rc_priv, txrc); +} + +static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta) +{ + struct ath_softc *sc = priv; + struct ath_rate_priv *ath_rc_priv = priv_sta; + const struct ath_rate_table *rate_table = NULL; + bool is_cw40, is_sgi40; + int i, j = 0; + + for (i = 0; i < sband->n_bitrates; i++) { + if (sta->supp_rates[sband->band] & BIT(i)) { + ath_rc_priv->neg_rates.rs_rates[j] + = (sband->bitrates[i].bitrate * 2) / 10; + j++; + } + } + ath_rc_priv->neg_rates.rs_nrates = j; + + if (sta->ht_cap.ht_supported) { + for (i = 0, j = 0; i < 77; i++) { + if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8))) + ath_rc_priv->neg_ht_rates.rs_rates[j++] = i; + if (j == ATH_RATE_MAX) + break; + } + ath_rc_priv->neg_ht_rates.rs_nrates = j; + } + + is_cw40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; + is_sgi40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40; + + /* Choose rate table first */ + + if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) || + (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) || + (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)) { + rate_table = ath_choose_rate_table(sc, sband->band, + sta->ht_cap.ht_supported, + is_cw40); + } else if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { + /* cur_rate_table would be set on init through config() */ + rate_table = sc->cur_rate_table; + } + + ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi40); + ath_rc_init(sc, priv_sta, sband, sta, rate_table); +} + +static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta, + u32 changed) +{ + struct ath_softc *sc = priv; + struct ath_rate_priv *ath_rc_priv = priv_sta; + const struct ath_rate_table *rate_table = NULL; + bool oper_cw40 = false, oper_sgi40; + bool local_cw40 = (ath_rc_priv->ht_cap & WLAN_RC_40_FLAG) ? + true : false; + bool local_sgi40 = (ath_rc_priv->ht_cap & WLAN_RC_SGI_FLAG) ? + true : false; + + /* FIXME: Handle AP mode later when we support CWM */ + + if (changed & IEEE80211_RC_HT_CHANGED) { + if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION) + return; + + if (sc->hw->conf.channel_type == NL80211_CHAN_HT40MINUS || + sc->hw->conf.channel_type == NL80211_CHAN_HT40PLUS) + oper_cw40 = true; + + oper_sgi40 = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? + true : false; + + if ((local_cw40 != oper_cw40) || (local_sgi40 != oper_sgi40)) { + rate_table = ath_choose_rate_table(sc, sband->band, + sta->ht_cap.ht_supported, + oper_cw40); + ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, + oper_cw40, oper_sgi40); + ath_rc_init(sc, priv_sta, sband, sta, rate_table); + + DPRINTF(sc, ATH_DBG_CONFIG, + "Operating HT Bandwidth changed to: %d\n", + sc->hw->conf.channel_type); + } + } +} + +static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) +{ + struct ath_wiphy *aphy = hw->priv; + return aphy->sc; +} + +static void ath_rate_free(void *priv) +{ + return; +} + +static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) +{ + struct ath_softc *sc = priv; + struct ath_rate_priv *rate_priv; + + rate_priv = kzalloc(sizeof(struct ath_rate_priv), gfp); + if (!rate_priv) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to allocate private rc structure\n"); + return NULL; + } + + rate_priv->rssi_down_time = jiffies_to_msecs(jiffies); + rate_priv->tx_triglevel_max = sc->sc_ah->caps.tx_triglevel_max; + + return rate_priv; +} + +static void ath_rate_free_sta(void *priv, struct ieee80211_sta *sta, + void *priv_sta) +{ + struct ath_rate_priv *rate_priv = priv_sta; + kfree(rate_priv); +} + +static struct rate_control_ops ath_rate_ops = { + .module = NULL, + .name = "ath9k_rate_control", + .tx_status = ath_tx_status, + .get_rate = ath_get_rate, + .rate_init = ath_rate_init, + .rate_update = ath_rate_update, + .alloc = ath_rate_alloc, + .free = ath_rate_free, + .alloc_sta = ath_rate_alloc_sta, + .free_sta = ath_rate_free_sta, +}; + +void ath_rate_attach(struct ath_softc *sc) +{ + sc->hw_rate_table[ATH9K_MODE_11B] = + &ar5416_11b_ratetable; + sc->hw_rate_table[ATH9K_MODE_11A] = + &ar5416_11a_ratetable; + sc->hw_rate_table[ATH9K_MODE_11G] = + &ar5416_11g_ratetable; + sc->hw_rate_table[ATH9K_MODE_11NA_HT20] = + &ar5416_11na_ratetable; + sc->hw_rate_table[ATH9K_MODE_11NG_HT20] = + &ar5416_11ng_ratetable; + sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS] = + &ar5416_11na_ratetable; + sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS] = + &ar5416_11na_ratetable; + sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS] = + &ar5416_11ng_ratetable; + sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS] = + &ar5416_11ng_ratetable; +} + +int ath_rate_control_register(void) +{ + return ieee80211_rate_control_register(&ath_rate_ops); +} + +void ath_rate_control_unregister(void) +{ + ieee80211_rate_control_unregister(&ath_rate_ops); +} diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h new file mode 100644 index 00000000000..e3abd76103f --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2004 Sam Leffler, Errno Consulting + * Copyright (c) 2004 Video54 Technologies, Inc. + * Copyright (c) 2008-2009 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 RC_H +#define RC_H + +struct ath_softc; + +#define ATH_RATE_MAX 30 +#define RATE_TABLE_SIZE 64 +#define MAX_TX_RATE_PHY 48 + +/* VALID_ALL - valid for 20/40/Legacy, + * VALID - Legacy only, + * VALID_20 - HT 20 only, + * VALID_40 - HT 40 only */ + +#define INVALID 0x0 +#define VALID 0x1 +#define VALID_20 0x2 +#define VALID_40 0x4 +#define VALID_2040 (VALID_20|VALID_40) +#define VALID_ALL (VALID_2040|VALID) + +enum { + WLAN_RC_PHY_OFDM, + WLAN_RC_PHY_CCK, + WLAN_RC_PHY_HT_20_SS, + WLAN_RC_PHY_HT_20_DS, + WLAN_RC_PHY_HT_40_SS, + WLAN_RC_PHY_HT_40_DS, + WLAN_RC_PHY_HT_20_SS_HGI, + WLAN_RC_PHY_HT_20_DS_HGI, + WLAN_RC_PHY_HT_40_SS_HGI, + WLAN_RC_PHY_HT_40_DS_HGI, + WLAN_RC_PHY_MAX +}; + +#define WLAN_RC_PHY_DS(_phy) ((_phy == WLAN_RC_PHY_HT_20_DS) \ + || (_phy == WLAN_RC_PHY_HT_40_DS) \ + || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)) +#define WLAN_RC_PHY_40(_phy) ((_phy == WLAN_RC_PHY_HT_40_SS) \ + || (_phy == WLAN_RC_PHY_HT_40_DS) \ + || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)) +#define WLAN_RC_PHY_SGI(_phy) ((_phy == WLAN_RC_PHY_HT_20_SS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)) + +#define WLAN_RC_PHY_HT(_phy) (_phy >= WLAN_RC_PHY_HT_20_SS) + +#define WLAN_RC_CAP_MODE(capflag) (((capflag & WLAN_RC_HT_FLAG) ? \ + (capflag & WLAN_RC_40_FLAG) ? VALID_40 : VALID_20 : VALID)) + +/* Return TRUE if flag supports HT20 && client supports HT20 or + * return TRUE if flag supports HT40 && client supports HT40. + * This is used becos some rates overlap between HT20/HT40. + */ +#define WLAN_RC_PHY_HT_VALID(flag, capflag) \ + (((flag & VALID_20) && !(capflag & WLAN_RC_40_FLAG)) || \ + ((flag & VALID_40) && (capflag & WLAN_RC_40_FLAG))) + +#define WLAN_RC_DS_FLAG (0x01) +#define WLAN_RC_40_FLAG (0x02) +#define WLAN_RC_SGI_FLAG (0x04) +#define WLAN_RC_HT_FLAG (0x08) + +/** + * struct ath_rate_table - Rate Control table + * @valid: valid for use in rate control + * @valid_single_stream: valid for use in rate control for + * single stream operation + * @phy: CCK/OFDM + * @ratekbps: rate in Kbits per second + * @user_ratekbps: user rate in Kbits per second + * @ratecode: rate that goes into HW descriptors + * @short_preamble: Mask for enabling short preamble in ratecode for CCK + * @dot11rate: value that goes into supported + * rates info element of MLME + * @ctrl_rate: Index of next lower basic rate, used for duration computation + * @max_4ms_framelen: maximum frame length(bytes) for tx duration + * @probe_interval: interval for rate control to probe for other rates + * @rssi_reduce_interval: interval for rate control to reduce rssi + * @initial_ratemax: initial ratemax value + */ +struct ath_rate_table { + int rate_cnt; + struct { + int valid; + int valid_single_stream; + u8 phy; + u32 ratekbps; + u32 user_ratekbps; + u8 ratecode; + u8 short_preamble; + u8 dot11rate; + u8 ctrl_rate; + int8_t rssi_ack_validmin; + int8_t rssi_ack_deltamin; + u8 base_index; + u8 cw40index; + u8 sgi_index; + u8 ht_index; + u32 max_4ms_framelen; + } info[RATE_TABLE_SIZE]; + u32 probe_interval; + u32 rssi_reduce_interval; + u8 initial_ratemax; +}; + +struct ath_tx_ratectrl_state { + int8_t rssi_thres; /* required rssi for this rate (dB) */ + u8 per; /* recent estimate of packet error rate (%) */ +}; + +struct ath_rateset { + u8 rs_nrates; + u8 rs_rates[ATH_RATE_MAX]; +}; + +/** + * struct ath_rate_priv - Rate Control priv data + * @state: RC state + * @rssi_last: last ACK rssi + * @rssi_last_lookup: last ACK rssi used for lookup + * @rssi_last_prev: previous last ACK rssi + * @rssi_last_prev2: 2nd previous last ACK rssi + * @rssi_sum_cnt: count of rssi_sum for averaging + * @rssi_sum_rate: rate that we are averaging + * @rssi_sum: running sum of rssi for averaging + * @probe_rate: rate we are probing at + * @rssi_time: msec timestamp for last ack rssi + * @rssi_down_time: msec timestamp for last down step + * @probe_time: msec timestamp for last probe + * @hw_maxretry_pktcnt: num of packets since we got HW max retry error + * @max_valid_rate: maximum number of valid rate + * @per_down_time: msec timestamp for last PER down step + * @valid_phy_ratecnt: valid rate count + * @rate_max_phy: phy index for the max rate + * @probe_interval: interval for ratectrl to probe for other rates + * @prev_data_rix: rate idx of last data frame + * @ht_cap: HT capabilities + * @neg_rates: Negotatied rates + * @neg_ht_rates: Negotiated HT rates + */ +struct ath_rate_priv { + int8_t rssi_last; + int8_t rssi_last_lookup; + int8_t rssi_last_prev; + int8_t rssi_last_prev2; + int32_t rssi_sum_cnt; + int32_t rssi_sum_rate; + int32_t rssi_sum; + u8 rate_table_size; + u8 probe_rate; + u8 hw_maxretry_pktcnt; + u8 max_valid_rate; + u8 valid_rate_index[RATE_TABLE_SIZE]; + u8 ht_cap; + u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX]; + u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][RATE_TABLE_SIZE]; + u8 rate_max_phy; + u32 rssi_time; + u32 rssi_down_time; + u32 probe_time; + u32 per_down_time; + u32 probe_interval; + u32 prev_data_rix; + u32 tx_triglevel_max; + struct ath_tx_ratectrl_state state[RATE_TABLE_SIZE]; + struct ath_rateset neg_rates; + struct ath_rateset neg_ht_rates; + struct ath_rate_softc *asc; +}; + +enum ath9k_internal_frame_type { + ATH9K_NOT_INTERNAL, + ATH9K_INT_PAUSE, + ATH9K_INT_UNPAUSE +}; + +struct ath_tx_info_priv { + struct ath_wiphy *aphy; + struct ath_tx_status tx; + int n_frames; + int n_bad_frames; + bool update_rc; + enum ath9k_internal_frame_type frame_type; +}; + +#define ATH_TX_INFO_PRIV(tx_info) \ + ((struct ath_tx_info_priv *)((tx_info)->rate_driver_data[0])) + +void ath_rate_attach(struct ath_softc *sc); +u8 ath_rate_findrateix(struct ath_softc *sc, u8 dot11_rate); +int ath_rate_control_register(void); +void ath_rate_control_unregister(void); + +#endif /* RC_H */ diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c new file mode 100644 index 00000000000..5014a19b0f7 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -0,0 +1,834 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +static struct ieee80211_hw * ath_get_virt_hw(struct ath_softc *sc, + struct ieee80211_hdr *hdr) +{ + struct ieee80211_hw *hw = sc->pri_wiphy->hw; + int i; + + spin_lock_bh(&sc->wiphy_lock); + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + if (aphy == NULL) + continue; + if (compare_ether_addr(hdr->addr1, aphy->hw->wiphy->perm_addr) + == 0) { + hw = aphy->hw; + break; + } + } + spin_unlock_bh(&sc->wiphy_lock); + return hw; +} + +/* + * Setup and link descriptors. + * + * 11N: we can no longer afford to self link the last descriptor. + * MAC acknowledges BA status as long as it copies frames to host + * buffer (or rx fifo). This can incorrectly acknowledge packets + * to a sender if last desc is self-linked. + */ +static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_desc *ds; + struct sk_buff *skb; + + ATH_RXBUF_RESET(bf); + + ds = bf->bf_desc; + ds->ds_link = 0; /* link to null */ + ds->ds_data = bf->bf_buf_addr; + + /* virtual addr of the beginning of the buffer. */ + skb = bf->bf_mpdu; + ASSERT(skb != NULL); + ds->ds_vdata = skb->data; + + /* setup rx descriptors. The rx.bufsize here tells the harware + * how much data it can DMA to us and that we are prepared + * to process */ + ath9k_hw_setuprxdesc(ah, ds, + sc->rx.bufsize, + 0); + + if (sc->rx.rxlink == NULL) + ath9k_hw_putrxbuf(ah, bf->bf_daddr); + else + *sc->rx.rxlink = bf->bf_daddr; + + sc->rx.rxlink = &ds->ds_link; + ath9k_hw_rxena(ah); +} + +static void ath_setdefantenna(struct ath_softc *sc, u32 antenna) +{ + /* XXX block beacon interrupts */ + ath9k_hw_setantenna(sc->sc_ah, antenna); + sc->rx.defant = antenna; + sc->rx.rxotherant = 0; +} + +/* + * Extend 15-bit time stamp from rx descriptor to + * a full 64-bit TSF using the current h/w TSF. +*/ +static 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; +} + +static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc, u32 len, gfp_t gfp_mask) +{ + struct sk_buff *skb; + u32 off; + + /* + * Cache-line-align. This is important (for the + * 5210 at least) as not doing so causes bogus data + * in rx'd frames. + */ + + /* Note: the kernel can allocate a value greater than + * what we ask it to give us. We really only need 4 KB as that + * is this hardware supports and in fact we need at least 3849 + * as that is the MAX AMSDU size this hardware supports. + * Unfortunately this means we may get 8 KB here from the + * kernel... and that is actually what is observed on some + * systems :( */ + skb = __dev_alloc_skb(len + sc->cachelsz - 1, gfp_mask); + if (skb != NULL) { + off = ((unsigned long) skb->data) % sc->cachelsz; + if (off != 0) + skb_reserve(skb, sc->cachelsz - off); + } else { + DPRINTF(sc, ATH_DBG_FATAL, + "skbuff alloc of size %u failed\n", len); + return NULL; + } + + return skb; +} + +/* + * For Decrypt or Demic errors, we only mark packet status here and always push + * up the frame up to let mac80211 handle the actual error case, be it no + * decryption key or real decryption error. This let us keep statistics there. + */ +static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds, + struct ieee80211_rx_status *rx_status, bool *decrypt_error, + struct ath_softc *sc) +{ + struct ieee80211_hdr *hdr; + u8 ratecode; + __le16 fc; + struct ieee80211_hw *hw; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); + hw = ath_get_virt_hw(sc, hdr); + + if (ds->ds_rxstat.rs_more) { + /* + * Frame spans multiple descriptors; this cannot happen yet + * as we don't support jumbograms. If not in monitor mode, + * discard the frame. Enable this if you want to see + * error frames in Monitor mode. + */ + if (sc->sc_ah->opmode != NL80211_IFTYPE_MONITOR) + goto rx_next; + } else if (ds->ds_rxstat.rs_status != 0) { + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY) + goto rx_next; + + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) { + *decrypt_error = true; + } else if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) { + if (ieee80211_is_ctl(fc)) + /* + * Sometimes, we get invalid + * MIC failures on valid control frames. + * Remove these mic errors. + */ + ds->ds_rxstat.rs_status &= ~ATH9K_RXERR_MIC; + else + rx_status->flag |= RX_FLAG_MMIC_ERROR; + } + /* + * Reject error frames with the exception of + * decryption and MIC failures. For monitor mode, + * we also ignore the CRC error. + */ + if (sc->sc_ah->opmode == NL80211_IFTYPE_MONITOR) { + if (ds->ds_rxstat.rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | + ATH9K_RXERR_CRC)) + goto rx_next; + } else { + if (ds->ds_rxstat.rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) { + goto rx_next; + } + } + } + + ratecode = ds->ds_rxstat.rs_rate; + + if (ratecode & 0x80) { + /* HT rate */ + rx_status->flag |= RX_FLAG_HT; + if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040) + rx_status->flag |= RX_FLAG_40MHZ; + if (ds->ds_rxstat.rs_flags & ATH9K_RX_GI) + rx_status->flag |= RX_FLAG_SHORT_GI; + rx_status->rate_idx = ratecode & 0x7f; + } else { + int i = 0, cur_band, n_rates; + + cur_band = hw->conf.channel->band; + n_rates = sc->sbands[cur_band].n_bitrates; + + for (i = 0; i < n_rates; i++) { + if (sc->sbands[cur_band].bitrates[i].hw_value == + ratecode) { + rx_status->rate_idx = i; + break; + } + + if (sc->sbands[cur_band].bitrates[i].hw_value_short == + ratecode) { + rx_status->rate_idx = i; + rx_status->flag |= RX_FLAG_SHORTPRE; + break; + } + } + } + + rx_status->mactime = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp); + rx_status->band = hw->conf.channel->band; + rx_status->freq = hw->conf.channel->center_freq; + rx_status->noise = sc->ani.noise_floor; + rx_status->signal = rx_status->noise + ds->ds_rxstat.rs_rssi; + rx_status->antenna = ds->ds_rxstat.rs_antenna; + + /* at 45 you will be able to use MCS 15 reliably. A more elaborate + * scheme can be used here but it requires tables of SNR/throughput for + * each possible mode used. */ + rx_status->qual = ds->ds_rxstat.rs_rssi * 100 / 45; + + /* rssi can be more than 45 though, anything above that + * should be considered at 100% */ + if (rx_status->qual > 100) + rx_status->qual = 100; + + rx_status->flag |= RX_FLAG_TSFT; + + return 1; +rx_next: + return 0; +} + +static void ath_opmode_init(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + u32 rfilt, mfilt[2]; + + /* configure rx filter */ + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); + + /* configure bssid mask */ + if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + ath9k_hw_setbssidmask(sc); + + /* configure operational mode */ + ath9k_hw_setopmode(ah); + + /* Handle any link-level address change. */ + ath9k_hw_setmac(ah, sc->sc_ah->macaddr); + + /* calculate and install multicast filter */ + mfilt[0] = mfilt[1] = ~0; + ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]); +} + +int ath_rx_init(struct ath_softc *sc, int nbufs) +{ + struct sk_buff *skb; + struct ath_buf *bf; + int error = 0; + + spin_lock_init(&sc->rx.rxflushlock); + sc->sc_flags &= ~SC_OP_RXFLUSH; + spin_lock_init(&sc->rx.rxbuflock); + + sc->rx.bufsize = roundup(IEEE80211_MAX_MPDU_LEN, + min(sc->cachelsz, (u16)64)); + + DPRINTF(sc, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n", + sc->cachelsz, sc->rx.bufsize); + + /* Initialize rx descriptors */ + + error = ath_descdma_setup(sc, &sc->rx.rxdma, &sc->rx.rxbuf, + "rx", nbufs, 1); + if (error != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "failed to allocate rx descriptors: %d\n", error); + goto err; + } + + list_for_each_entry(bf, &sc->rx.rxbuf, list) { + skb = ath_rxbuf_alloc(sc, sc->rx.bufsize, GFP_KERNEL); + if (skb == NULL) { + error = -ENOMEM; + goto err; + } + + bf->bf_mpdu = skb; + bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, + sc->rx.bufsize, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(sc->dev, + bf->bf_buf_addr))) { + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + DPRINTF(sc, ATH_DBG_FATAL, + "dma_mapping_error() on RX init\n"); + error = -ENOMEM; + goto err; + } + bf->bf_dmacontext = bf->bf_buf_addr; + } + sc->rx.rxlink = NULL; + +err: + if (error) + ath_rx_cleanup(sc); + + return error; +} + +void ath_rx_cleanup(struct ath_softc *sc) +{ + struct sk_buff *skb; + struct ath_buf *bf; + + list_for_each_entry(bf, &sc->rx.rxbuf, list) { + skb = bf->bf_mpdu; + if (skb) { + dma_unmap_single(sc->dev, bf->bf_buf_addr, + sc->rx.bufsize, DMA_FROM_DEVICE); + dev_kfree_skb(skb); + } + } + + if (sc->rx.rxdma.dd_desc_len != 0) + ath_descdma_cleanup(sc, &sc->rx.rxdma, &sc->rx.rxbuf); +} + +/* + * Calculate the receive filter according to the + * operating mode and state: + * + * o always accept unicast, broadcast, and multicast traffic + * o maintain current state of phy error reception (the hal + * may enable phy error frames for noise immunity work) + * o probe request frames are accepted only when operating in + * hostap, adhoc, or monitor modes + * o enable promiscuous mode according to the interface state + * o accept beacons: + * - when operating in adhoc mode so the 802.11 layer creates + * node table entries for peers, + * - when operating in station mode for collecting rssi data when + * the station is otherwise quiet, or + * - when operating as a repeater so we see repeater-sta beacons + * - when scanning + */ + +u32 ath_calcrxfilter(struct ath_softc *sc) +{ +#define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR) + + u32 rfilt; + + rfilt = (ath9k_hw_getrxfilter(sc->sc_ah) & RX_FILTER_PRESERVE) + | ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST + | ATH9K_RX_FILTER_MCAST; + + /* If not a STA, enable processing of Probe Requests */ + if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION) + rfilt |= ATH9K_RX_FILTER_PROBEREQ; + + /* + * Set promiscuous mode when FIF_PROMISC_IN_BSS is enabled for station + * mode interface or when in monitor mode. AP mode does not need this + * since it receives all in-BSS frames anyway. + */ + if (((sc->sc_ah->opmode != NL80211_IFTYPE_AP) && + (sc->rx.rxfilter & FIF_PROMISC_IN_BSS)) || + (sc->sc_ah->opmode == NL80211_IFTYPE_MONITOR)) + rfilt |= ATH9K_RX_FILTER_PROM; + + if (sc->rx.rxfilter & FIF_CONTROL) + rfilt |= ATH9K_RX_FILTER_CONTROL; + + if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) && + !(sc->rx.rxfilter & FIF_BCN_PRBRESP_PROMISC)) + rfilt |= ATH9K_RX_FILTER_MYBEACON; + else + rfilt |= ATH9K_RX_FILTER_BEACON; + + /* If in HOSTAP mode, want to enable reception of PSPOLL frames */ + if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) + rfilt |= ATH9K_RX_FILTER_PSPOLL; + + if (sc->sec_wiphy) { + /* TODO: only needed if more than one BSSID is in use in + * station/adhoc mode */ + /* TODO: for older chips, may need to add ATH9K_RX_FILTER_PROM + */ + rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL; + } + + return rfilt; + +#undef RX_FILTER_PRESERVE +} + +int ath_startrecv(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_buf *bf, *tbf; + + spin_lock_bh(&sc->rx.rxbuflock); + if (list_empty(&sc->rx.rxbuf)) + goto start_recv; + + sc->rx.rxlink = NULL; + list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) { + ath_rx_buf_link(sc, bf); + } + + /* We could have deleted elements so the list may be empty now */ + if (list_empty(&sc->rx.rxbuf)) + goto start_recv; + + bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); + ath9k_hw_putrxbuf(ah, bf->bf_daddr); + ath9k_hw_rxena(ah); + +start_recv: + spin_unlock_bh(&sc->rx.rxbuflock); + ath_opmode_init(sc); + ath9k_hw_startpcureceive(ah); + + return 0; +} + +bool ath_stoprecv(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + bool stopped; + + ath9k_hw_stoppcurecv(ah); + ath9k_hw_setrxfilter(ah, 0); + stopped = ath9k_hw_stopdmarecv(ah); + sc->rx.rxlink = NULL; + + return stopped; +} + +void ath_flushrecv(struct ath_softc *sc) +{ + spin_lock_bh(&sc->rx.rxflushlock); + sc->sc_flags |= SC_OP_RXFLUSH; + ath_rx_tasklet(sc, 1); + sc->sc_flags &= ~SC_OP_RXFLUSH; + spin_unlock_bh(&sc->rx.rxflushlock); +} + +static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) +{ + /* Check whether the Beacon frame has DTIM indicating buffered bc/mc */ + struct ieee80211_mgmt *mgmt; + u8 *pos, *end, id, elen; + struct ieee80211_tim_ie *tim; + + mgmt = (struct ieee80211_mgmt *)skb->data; + pos = mgmt->u.beacon.variable; + end = skb->data + skb->len; + + while (pos + 2 < end) { + id = *pos++; + elen = *pos++; + if (pos + elen > end) + break; + + if (id == WLAN_EID_TIM) { + if (elen < sizeof(*tim)) + break; + tim = (struct ieee80211_tim_ie *) pos; + if (tim->dtim_count != 0) + break; + return tim->bitmap_ctrl & 0x01; + } + + pos += elen; + } + + return false; +} + +static void ath_rx_ps_back_to_sleep(struct ath_softc *sc) +{ + sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB); +} + +static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt; + + if (skb->len < 24 + 8 + 2 + 2) + return; + + mgmt = (struct ieee80211_mgmt *)skb->data; + if (memcmp(sc->curbssid, mgmt->bssid, ETH_ALEN) != 0) + return; /* not from our current AP */ + + if (sc->sc_flags & SC_OP_BEACON_SYNC) { + sc->sc_flags &= ~SC_OP_BEACON_SYNC; + DPRINTF(sc, ATH_DBG_PS, "Reconfigure Beacon timers based on " + "timestamp from the AP\n"); + ath_beacon_config(sc, NULL); + } + + if (!(sc->hw->conf.flags & IEEE80211_CONF_PS)) { + /* We are not in PS mode anymore; remain awake */ + DPRINTF(sc, ATH_DBG_PS, "Not in PS mode anymore, remain " + "awake\n"); + sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB); + return; + } + + if (ath_beacon_dtim_pending_cab(skb)) { + /* + * Remain awake waiting for buffered broadcast/multicast + * frames. + */ + DPRINTF(sc, ATH_DBG_PS, "Received DTIM beacon indicating " + "buffered broadcast/multicast frame(s)\n"); + sc->sc_flags |= SC_OP_WAIT_FOR_CAB; + return; + } + + if (sc->sc_flags & SC_OP_WAIT_FOR_CAB) { + /* + * This can happen if a broadcast frame is dropped or the AP + * fails to send a frame indicating that all CAB frames have + * been delivered. + */ + DPRINTF(sc, ATH_DBG_PS, "PS wait for CAB frames timed out\n"); + } + + /* No more broadcast/multicast frames to be received at this point. */ + ath_rx_ps_back_to_sleep(sc); +} + +static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *)skb->data; + + /* Process Beacon and CAB receive in PS state */ + if ((sc->sc_flags & SC_OP_WAIT_FOR_BEACON) && + ieee80211_is_beacon(hdr->frame_control)) + ath_rx_ps_beacon(sc, skb); + else if ((sc->sc_flags & SC_OP_WAIT_FOR_CAB) && + (ieee80211_is_data(hdr->frame_control) || + ieee80211_is_action(hdr->frame_control)) && + is_multicast_ether_addr(hdr->addr1) && + !ieee80211_has_moredata(hdr->frame_control)) { + DPRINTF(sc, ATH_DBG_PS, "All PS CAB frames received, back to " + "sleep\n"); + /* + * No more broadcast/multicast frames to be received at this + * point. + */ + ath_rx_ps_back_to_sleep(sc); + } else if ((sc->sc_flags & SC_OP_WAIT_FOR_PSPOLL_DATA) && + !is_multicast_ether_addr(hdr->addr1) && + !ieee80211_has_morefrags(hdr->frame_control)) { + sc->sc_flags &= ~SC_OP_WAIT_FOR_PSPOLL_DATA; + DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having " + "received PS-Poll data (0x%x)\n", + sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | + SC_OP_WAIT_FOR_CAB | + SC_OP_WAIT_FOR_PSPOLL_DATA | + SC_OP_WAIT_FOR_TX_ACK)); + } +} + +static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *)skb->data; + + /* Send the frame to mac80211 */ + if (is_multicast_ether_addr(hdr->addr1)) { + int i; + /* + * Deliver broadcast/multicast frames to all suitable + * virtual wiphys. + */ + /* TODO: filter based on channel configuration */ + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + struct sk_buff *nskb; + if (aphy == NULL) + continue; + nskb = skb_copy(skb, GFP_ATOMIC); + if (nskb) + __ieee80211_rx(aphy->hw, nskb, rx_status); + } + __ieee80211_rx(sc->hw, skb, rx_status); + } else { + /* Deliver unicast frames based on receiver address */ + __ieee80211_rx(ath_get_virt_hw(sc, hdr), skb, rx_status); + } +} + +int ath_rx_tasklet(struct ath_softc *sc, int flush) +{ +#define PA2DESC(_sc, _pa) \ + ((struct ath_desc *)((caddr_t)(_sc)->rx.rxdma.dd_desc + \ + ((_pa) - (_sc)->rx.rxdma.dd_desc_paddr))) + + struct ath_buf *bf; + struct ath_desc *ds; + struct sk_buff *skb = NULL, *requeue_skb; + struct ieee80211_rx_status rx_status; + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_hdr *hdr; + int hdrlen, padsize, retval; + bool decrypt_error = false; + u8 keyix; + __le16 fc; + + spin_lock_bh(&sc->rx.rxbuflock); + + do { + /* If handling rx interrupt and flush is in progress => exit */ + if ((sc->sc_flags & SC_OP_RXFLUSH) && (flush == 0)) + break; + + if (list_empty(&sc->rx.rxbuf)) { + sc->rx.rxlink = NULL; + break; + } + + bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); + ds = bf->bf_desc; + + /* + * Must provide the virtual address of the current + * descriptor, the physical address, and the virtual + * address of the next descriptor in the h/w chain. + * This allows the HAL to look ahead to see if the + * hardware is done with a descriptor by checking the + * done bit in the following descriptor and the address + * of the current descriptor the DMA engine is working + * on. All this is necessary because of our use of + * a self-linked list to avoid rx overruns. + */ + retval = ath9k_hw_rxprocdesc(ah, ds, + bf->bf_daddr, + PA2DESC(sc, ds->ds_link), + 0); + if (retval == -EINPROGRESS) { + struct ath_buf *tbf; + struct ath_desc *tds; + + if (list_is_last(&bf->list, &sc->rx.rxbuf)) { + sc->rx.rxlink = NULL; + break; + } + + tbf = list_entry(bf->list.next, struct ath_buf, list); + + /* + * On some hardware the descriptor status words could + * get corrupted, including the done bit. Because of + * this, check if the next descriptor's done bit is + * set or not. + * + * If the next descriptor's done bit is set, the current + * descriptor has been corrupted. Force s/w to discard + * this descriptor and continue... + */ + + tds = tbf->bf_desc; + retval = ath9k_hw_rxprocdesc(ah, tds, tbf->bf_daddr, + PA2DESC(sc, tds->ds_link), 0); + if (retval == -EINPROGRESS) { + break; + } + } + + skb = bf->bf_mpdu; + if (!skb) + continue; + + /* + * Synchronize the DMA transfer with CPU before + * 1. accessing the frame + * 2. requeueing the same buffer to h/w + */ + dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr, + sc->rx.bufsize, + DMA_FROM_DEVICE); + + /* + * If we're asked to flush receive queue, directly + * chain it back at the queue without processing it. + */ + if (flush) + goto requeue; + + if (!ds->ds_rxstat.rs_datalen) + goto requeue; + + /* The status portion of the descriptor could get corrupted. */ + if (sc->rx.bufsize < ds->ds_rxstat.rs_datalen) + goto requeue; + + if (!ath_rx_prepare(skb, ds, &rx_status, &decrypt_error, sc)) + goto requeue; + + /* Ensure we always have an skb to requeue once we are done + * processing the current buffer's skb */ + requeue_skb = ath_rxbuf_alloc(sc, sc->rx.bufsize, GFP_ATOMIC); + + /* If there is no memory we ignore the current RX'd frame, + * tell hardware it can give us a new frame using the old + * skb and put it at the tail of the sc->rx.rxbuf list for + * processing. */ + if (!requeue_skb) + goto requeue; + + /* Unmap the frame */ + dma_unmap_single(sc->dev, bf->bf_buf_addr, + sc->rx.bufsize, + DMA_FROM_DEVICE); + + skb_put(skb, ds->ds_rxstat.rs_datalen); + skb->protocol = cpu_to_be16(ETH_P_CONTROL); + + /* see if any padding is done by the hw and remove it */ + hdr = (struct ieee80211_hdr *)skb->data; + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + fc = hdr->frame_control; + + /* The MAC header is padded to have 32-bit boundary if the + * packet payload is non-zero. The general calculation for + * padsize would take into account odd header lengths: + * padsize = (4 - hdrlen % 4) % 4; However, since only + * even-length headers are used, padding can only be 0 or 2 + * bytes and we can optimize this a bit. In addition, we must + * not try to remove padding from short control frames that do + * not have payload. */ + padsize = hdrlen & 3; + if (padsize && hdrlen >= 24) { + memmove(skb->data + padsize, skb->data, hdrlen); + skb_pull(skb, padsize); + } + + keyix = ds->ds_rxstat.rs_keyix; + + if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error) { + rx_status.flag |= RX_FLAG_DECRYPTED; + } else if (ieee80211_has_protected(fc) + && !decrypt_error && skb->len >= hdrlen + 4) { + keyix = skb->data[hdrlen + 3] >> 6; + + if (test_bit(keyix, sc->keymap)) + rx_status.flag |= RX_FLAG_DECRYPTED; + } + if (ah->sw_mgmt_crypto && + (rx_status.flag & RX_FLAG_DECRYPTED) && + ieee80211_is_mgmt(fc)) { + /* Use software decrypt for management frames. */ + rx_status.flag &= ~RX_FLAG_DECRYPTED; + } + + /* We will now give hardware our shiny new allocated skb */ + bf->bf_mpdu = requeue_skb; + bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data, + sc->rx.bufsize, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(sc->dev, + bf->bf_buf_addr))) { + dev_kfree_skb_any(requeue_skb); + bf->bf_mpdu = NULL; + DPRINTF(sc, ATH_DBG_FATAL, + "dma_mapping_error() on RX\n"); + ath_rx_send_to_mac80211(sc, skb, &rx_status); + break; + } + bf->bf_dmacontext = bf->bf_buf_addr; + + /* + * change the default rx antenna if rx diversity chooses the + * other antenna 3 times in a row. + */ + if (sc->rx.defant != ds->ds_rxstat.rs_antenna) { + if (++sc->rx.rxotherant >= 3) + ath_setdefantenna(sc, ds->ds_rxstat.rs_antenna); + } else { + sc->rx.rxotherant = 0; + } + + if (unlikely(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | + SC_OP_WAIT_FOR_PSPOLL_DATA))) + ath_rx_ps(sc, skb); + + ath_rx_send_to_mac80211(sc, skb, &rx_status); + +requeue: + list_move_tail(&bf->list, &sc->rx.rxbuf); + ath_rx_buf_link(sc, bf); + } while (1); + + spin_unlock_bh(&sc->rx.rxbuflock); + + return 0; +#undef PA2DESC +} diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h new file mode 100644 index 00000000000..52605246679 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -0,0 +1,1511 @@ +/* + * Copyright (c) 2008-2009 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 REG_H +#define REG_H + +#define AR_CR 0x0008 +#define AR_CR_RXE 0x00000004 +#define AR_CR_RXD 0x00000020 +#define AR_CR_SWI 0x00000040 + +#define AR_RXDP 0x000C + +#define AR_CFG 0x0014 +#define AR_CFG_SWTD 0x00000001 +#define AR_CFG_SWTB 0x00000002 +#define AR_CFG_SWRD 0x00000004 +#define AR_CFG_SWRB 0x00000008 +#define AR_CFG_SWRG 0x00000010 +#define AR_CFG_AP_ADHOC_INDICATION 0x00000020 +#define AR_CFG_PHOK 0x00000100 +#define AR_CFG_CLK_GATE_DIS 0x00000400 +#define AR_CFG_EEBS 0x00000200 +#define AR_CFG_PCI_MASTER_REQ_Q_THRESH 0x00060000 +#define AR_CFG_PCI_MASTER_REQ_Q_THRESH_S 17 + +#define AR_MIRT 0x0020 +#define AR_MIRT_VAL 0x0000ffff +#define AR_MIRT_VAL_S 16 + +#define AR_IER 0x0024 +#define AR_IER_ENABLE 0x00000001 +#define AR_IER_DISABLE 0x00000000 + +#define AR_TIMT 0x0028 +#define AR_TIMT_LAST 0x0000ffff +#define AR_TIMT_LAST_S 0 +#define AR_TIMT_FIRST 0xffff0000 +#define AR_TIMT_FIRST_S 16 + +#define AR_RIMT 0x002C +#define AR_RIMT_LAST 0x0000ffff +#define AR_RIMT_LAST_S 0 +#define AR_RIMT_FIRST 0xffff0000 +#define AR_RIMT_FIRST_S 16 + +#define AR_DMASIZE_4B 0x00000000 +#define AR_DMASIZE_8B 0x00000001 +#define AR_DMASIZE_16B 0x00000002 +#define AR_DMASIZE_32B 0x00000003 +#define AR_DMASIZE_64B 0x00000004 +#define AR_DMASIZE_128B 0x00000005 +#define AR_DMASIZE_256B 0x00000006 +#define AR_DMASIZE_512B 0x00000007 + +#define AR_TXCFG 0x0030 +#define AR_TXCFG_DMASZ_MASK 0x00000007 +#define AR_TXCFG_DMASZ_4B 0 +#define AR_TXCFG_DMASZ_8B 1 +#define AR_TXCFG_DMASZ_16B 2 +#define AR_TXCFG_DMASZ_32B 3 +#define AR_TXCFG_DMASZ_64B 4 +#define AR_TXCFG_DMASZ_128B 5 +#define AR_TXCFG_DMASZ_256B 6 +#define AR_TXCFG_DMASZ_512B 7 +#define AR_FTRIG 0x000003F0 +#define AR_FTRIG_S 4 +#define AR_FTRIG_IMMED 0x00000000 +#define AR_FTRIG_64B 0x00000010 +#define AR_FTRIG_128B 0x00000020 +#define AR_FTRIG_192B 0x00000030 +#define AR_FTRIG_256B 0x00000040 +#define AR_FTRIG_512B 0x00000080 +#define AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY 0x00000800 + +#define AR_RXCFG 0x0034 +#define AR_RXCFG_CHIRP 0x00000008 +#define AR_RXCFG_ZLFDMA 0x00000010 +#define AR_RXCFG_DMASZ_MASK 0x00000007 +#define AR_RXCFG_DMASZ_4B 0 +#define AR_RXCFG_DMASZ_8B 1 +#define AR_RXCFG_DMASZ_16B 2 +#define AR_RXCFG_DMASZ_32B 3 +#define AR_RXCFG_DMASZ_64B 4 +#define AR_RXCFG_DMASZ_128B 5 +#define AR_RXCFG_DMASZ_256B 6 +#define AR_RXCFG_DMASZ_512B 7 + +#define AR_MIBC 0x0040 +#define AR_MIBC_COW 0x00000001 +#define AR_MIBC_FMC 0x00000002 +#define AR_MIBC_CMC 0x00000004 +#define AR_MIBC_MCS 0x00000008 + +#define AR_TOPS 0x0044 +#define AR_TOPS_MASK 0x0000FFFF + +#define AR_RXNPTO 0x0048 +#define AR_RXNPTO_MASK 0x000003FF + +#define AR_TXNPTO 0x004C +#define AR_TXNPTO_MASK 0x000003FF +#define AR_TXNPTO_QCU_MASK 0x000FFC00 + +#define AR_RPGTO 0x0050 +#define AR_RPGTO_MASK 0x000003FF + +#define AR_RPCNT 0x0054 +#define AR_RPCNT_MASK 0x0000001F + +#define AR_MACMISC 0x0058 +#define AR_MACMISC_PCI_EXT_FORCE 0x00000010 +#define AR_MACMISC_DMA_OBS 0x000001E0 +#define AR_MACMISC_DMA_OBS_S 5 +#define AR_MACMISC_DMA_OBS_LINE_0 0 +#define AR_MACMISC_DMA_OBS_LINE_1 1 +#define AR_MACMISC_DMA_OBS_LINE_2 2 +#define AR_MACMISC_DMA_OBS_LINE_3 3 +#define AR_MACMISC_DMA_OBS_LINE_4 4 +#define AR_MACMISC_DMA_OBS_LINE_5 5 +#define AR_MACMISC_DMA_OBS_LINE_6 6 +#define AR_MACMISC_DMA_OBS_LINE_7 7 +#define AR_MACMISC_DMA_OBS_LINE_8 8 +#define AR_MACMISC_MISC_OBS 0x00000E00 +#define AR_MACMISC_MISC_OBS_S 9 +#define AR_MACMISC_MISC_OBS_BUS_LSB 0x00007000 +#define AR_MACMISC_MISC_OBS_BUS_LSB_S 12 +#define AR_MACMISC_MISC_OBS_BUS_MSB 0x00038000 +#define AR_MACMISC_MISC_OBS_BUS_MSB_S 15 +#define AR_MACMISC_MISC_OBS_BUS_1 1 + +#define AR_GTXTO 0x0064 +#define AR_GTXTO_TIMEOUT_COUNTER 0x0000FFFF +#define AR_GTXTO_TIMEOUT_LIMIT 0xFFFF0000 +#define AR_GTXTO_TIMEOUT_LIMIT_S 16 + +#define AR_GTTM 0x0068 +#define AR_GTTM_USEC 0x00000001 +#define AR_GTTM_IGNORE_IDLE 0x00000002 +#define AR_GTTM_RESET_IDLE 0x00000004 +#define AR_GTTM_CST_USEC 0x00000008 + +#define AR_CST 0x006C +#define AR_CST_TIMEOUT_COUNTER 0x0000FFFF +#define AR_CST_TIMEOUT_LIMIT 0xFFFF0000 +#define AR_CST_TIMEOUT_LIMIT_S 16 + +#define AR_ISR 0x0080 +#define AR_ISR_RXOK 0x00000001 +#define AR_ISR_RXDESC 0x00000002 +#define AR_ISR_RXERR 0x00000004 +#define AR_ISR_RXNOPKT 0x00000008 +#define AR_ISR_RXEOL 0x00000010 +#define AR_ISR_RXORN 0x00000020 +#define AR_ISR_TXOK 0x00000040 +#define AR_ISR_TXDESC 0x00000080 +#define AR_ISR_TXERR 0x00000100 +#define AR_ISR_TXNOPKT 0x00000200 +#define AR_ISR_TXEOL 0x00000400 +#define AR_ISR_TXURN 0x00000800 +#define AR_ISR_MIB 0x00001000 +#define AR_ISR_SWI 0x00002000 +#define AR_ISR_RXPHY 0x00004000 +#define AR_ISR_RXKCM 0x00008000 +#define AR_ISR_SWBA 0x00010000 +#define AR_ISR_BRSSI 0x00020000 +#define AR_ISR_BMISS 0x00040000 +#define AR_ISR_BNR 0x00100000 +#define AR_ISR_RXCHIRP 0x00200000 +#define AR_ISR_BCNMISC 0x00800000 +#define AR_ISR_TIM 0x00800000 +#define AR_ISR_QCBROVF 0x02000000 +#define AR_ISR_QCBRURN 0x04000000 +#define AR_ISR_QTRIG 0x08000000 +#define AR_ISR_GENTMR 0x10000000 + +#define AR_ISR_TXMINTR 0x00080000 +#define AR_ISR_RXMINTR 0x01000000 +#define AR_ISR_TXINTM 0x40000000 +#define AR_ISR_RXINTM 0x80000000 + +#define AR_ISR_S0 0x0084 +#define AR_ISR_S0_QCU_TXOK 0x000003FF +#define AR_ISR_S0_QCU_TXOK_S 0 +#define AR_ISR_S0_QCU_TXDESC 0x03FF0000 +#define AR_ISR_S0_QCU_TXDESC_S 16 + +#define AR_ISR_S1 0x0088 +#define AR_ISR_S1_QCU_TXERR 0x000003FF +#define AR_ISR_S1_QCU_TXERR_S 0 +#define AR_ISR_S1_QCU_TXEOL 0x03FF0000 +#define AR_ISR_S1_QCU_TXEOL_S 16 + +#define AR_ISR_S2 0x008c +#define AR_ISR_S2_QCU_TXURN 0x000003FF +#define AR_ISR_S2_CST 0x00400000 +#define AR_ISR_S2_GTT 0x00800000 +#define AR_ISR_S2_TIM 0x01000000 +#define AR_ISR_S2_CABEND 0x02000000 +#define AR_ISR_S2_DTIMSYNC 0x04000000 +#define AR_ISR_S2_BCNTO 0x08000000 +#define AR_ISR_S2_CABTO 0x10000000 +#define AR_ISR_S2_DTIM 0x20000000 +#define AR_ISR_S2_TSFOOR 0x40000000 +#define AR_ISR_S2_TBTT_TIME 0x80000000 + +#define AR_ISR_S3 0x0090 +#define AR_ISR_S3_QCU_QCBROVF 0x000003FF +#define AR_ISR_S3_QCU_QCBRURN 0x03FF0000 + +#define AR_ISR_S4 0x0094 +#define AR_ISR_S4_QCU_QTRIG 0x000003FF +#define AR_ISR_S4_RESV0 0xFFFFFC00 + +#define AR_ISR_S5 0x0098 +#define AR_ISR_S5_TIMER_TRIG 0x000000FF +#define AR_ISR_S5_TIMER_THRESH 0x0007FE00 +#define AR_ISR_S5_TIM_TIMER 0x00000010 +#define AR_ISR_S5_DTIM_TIMER 0x00000020 +#define AR_ISR_S5_S 0x00d8 +#define AR_IMR_S5 0x00b8 +#define AR_IMR_S5_TIM_TIMER 0x00000010 +#define AR_IMR_S5_DTIM_TIMER 0x00000020 + + +#define AR_IMR 0x00a0 +#define AR_IMR_RXOK 0x00000001 +#define AR_IMR_RXDESC 0x00000002 +#define AR_IMR_RXERR 0x00000004 +#define AR_IMR_RXNOPKT 0x00000008 +#define AR_IMR_RXEOL 0x00000010 +#define AR_IMR_RXORN 0x00000020 +#define AR_IMR_TXOK 0x00000040 +#define AR_IMR_TXDESC 0x00000080 +#define AR_IMR_TXERR 0x00000100 +#define AR_IMR_TXNOPKT 0x00000200 +#define AR_IMR_TXEOL 0x00000400 +#define AR_IMR_TXURN 0x00000800 +#define AR_IMR_MIB 0x00001000 +#define AR_IMR_SWI 0x00002000 +#define AR_IMR_RXPHY 0x00004000 +#define AR_IMR_RXKCM 0x00008000 +#define AR_IMR_SWBA 0x00010000 +#define AR_IMR_BRSSI 0x00020000 +#define AR_IMR_BMISS 0x00040000 +#define AR_IMR_BNR 0x00100000 +#define AR_IMR_RXCHIRP 0x00200000 +#define AR_IMR_BCNMISC 0x00800000 +#define AR_IMR_TIM 0x00800000 +#define AR_IMR_QCBROVF 0x02000000 +#define AR_IMR_QCBRURN 0x04000000 +#define AR_IMR_QTRIG 0x08000000 +#define AR_IMR_GENTMR 0x10000000 + +#define AR_IMR_TXMINTR 0x00080000 +#define AR_IMR_RXMINTR 0x01000000 +#define AR_IMR_TXINTM 0x40000000 +#define AR_IMR_RXINTM 0x80000000 + +#define AR_IMR_S0 0x00a4 +#define AR_IMR_S0_QCU_TXOK 0x000003FF +#define AR_IMR_S0_QCU_TXOK_S 0 +#define AR_IMR_S0_QCU_TXDESC 0x03FF0000 +#define AR_IMR_S0_QCU_TXDESC_S 16 + +#define AR_IMR_S1 0x00a8 +#define AR_IMR_S1_QCU_TXERR 0x000003FF +#define AR_IMR_S1_QCU_TXERR_S 0 +#define AR_IMR_S1_QCU_TXEOL 0x03FF0000 +#define AR_IMR_S1_QCU_TXEOL_S 16 + +#define AR_IMR_S2 0x00ac +#define AR_IMR_S2_QCU_TXURN 0x000003FF +#define AR_IMR_S2_QCU_TXURN_S 0 +#define AR_IMR_S2_CST 0x00400000 +#define AR_IMR_S2_GTT 0x00800000 +#define AR_IMR_S2_TIM 0x01000000 +#define AR_IMR_S2_CABEND 0x02000000 +#define AR_IMR_S2_DTIMSYNC 0x04000000 +#define AR_IMR_S2_BCNTO 0x08000000 +#define AR_IMR_S2_CABTO 0x10000000 +#define AR_IMR_S2_DTIM 0x20000000 +#define AR_IMR_S2_TSFOOR 0x40000000 + +#define AR_IMR_S3 0x00b0 +#define AR_IMR_S3_QCU_QCBROVF 0x000003FF +#define AR_IMR_S3_QCU_QCBRURN 0x03FF0000 +#define AR_IMR_S3_QCU_QCBRURN_S 16 + +#define AR_IMR_S4 0x00b4 +#define AR_IMR_S4_QCU_QTRIG 0x000003FF +#define AR_IMR_S4_RESV0 0xFFFFFC00 + +#define AR_IMR_S5 0x00b8 +#define AR_IMR_S5_TIMER_TRIG 0x000000FF +#define AR_IMR_S5_TIMER_THRESH 0x0000FF00 + + +#define AR_ISR_RAC 0x00c0 +#define AR_ISR_S0_S 0x00c4 +#define AR_ISR_S0_QCU_TXOK 0x000003FF +#define AR_ISR_S0_QCU_TXOK_S 0 +#define AR_ISR_S0_QCU_TXDESC 0x03FF0000 +#define AR_ISR_S0_QCU_TXDESC_S 16 + +#define AR_ISR_S1_S 0x00c8 +#define AR_ISR_S1_QCU_TXERR 0x000003FF +#define AR_ISR_S1_QCU_TXERR_S 0 +#define AR_ISR_S1_QCU_TXEOL 0x03FF0000 +#define AR_ISR_S1_QCU_TXEOL_S 16 + +#define AR_ISR_S2_S 0x00cc +#define AR_ISR_S3_S 0x00d0 +#define AR_ISR_S4_S 0x00d4 +#define AR_ISR_S5_S 0x00d8 +#define AR_DMADBG_0 0x00e0 +#define AR_DMADBG_1 0x00e4 +#define AR_DMADBG_2 0x00e8 +#define AR_DMADBG_3 0x00ec +#define AR_DMADBG_4 0x00f0 +#define AR_DMADBG_5 0x00f4 +#define AR_DMADBG_6 0x00f8 +#define AR_DMADBG_7 0x00fc + +#define AR_NUM_QCU 10 +#define AR_QCU_0 0x0001 +#define AR_QCU_1 0x0002 +#define AR_QCU_2 0x0004 +#define AR_QCU_3 0x0008 +#define AR_QCU_4 0x0010 +#define AR_QCU_5 0x0020 +#define AR_QCU_6 0x0040 +#define AR_QCU_7 0x0080 +#define AR_QCU_8 0x0100 +#define AR_QCU_9 0x0200 + +#define AR_Q0_TXDP 0x0800 +#define AR_Q1_TXDP 0x0804 +#define AR_Q2_TXDP 0x0808 +#define AR_Q3_TXDP 0x080c +#define AR_Q4_TXDP 0x0810 +#define AR_Q5_TXDP 0x0814 +#define AR_Q6_TXDP 0x0818 +#define AR_Q7_TXDP 0x081c +#define AR_Q8_TXDP 0x0820 +#define AR_Q9_TXDP 0x0824 +#define AR_QTXDP(_i) (AR_Q0_TXDP + ((_i)<<2)) + +#define AR_Q_TXE 0x0840 +#define AR_Q_TXE_M 0x000003FF + +#define AR_Q_TXD 0x0880 +#define AR_Q_TXD_M 0x000003FF + +#define AR_Q0_CBRCFG 0x08c0 +#define AR_Q1_CBRCFG 0x08c4 +#define AR_Q2_CBRCFG 0x08c8 +#define AR_Q3_CBRCFG 0x08cc +#define AR_Q4_CBRCFG 0x08d0 +#define AR_Q5_CBRCFG 0x08d4 +#define AR_Q6_CBRCFG 0x08d8 +#define AR_Q7_CBRCFG 0x08dc +#define AR_Q8_CBRCFG 0x08e0 +#define AR_Q9_CBRCFG 0x08e4 +#define AR_QCBRCFG(_i) (AR_Q0_CBRCFG + ((_i)<<2)) +#define AR_Q_CBRCFG_INTERVAL 0x00FFFFFF +#define AR_Q_CBRCFG_INTERVAL_S 0 +#define AR_Q_CBRCFG_OVF_THRESH 0xFF000000 +#define AR_Q_CBRCFG_OVF_THRESH_S 24 + +#define AR_Q0_RDYTIMECFG 0x0900 +#define AR_Q1_RDYTIMECFG 0x0904 +#define AR_Q2_RDYTIMECFG 0x0908 +#define AR_Q3_RDYTIMECFG 0x090c +#define AR_Q4_RDYTIMECFG 0x0910 +#define AR_Q5_RDYTIMECFG 0x0914 +#define AR_Q6_RDYTIMECFG 0x0918 +#define AR_Q7_RDYTIMECFG 0x091c +#define AR_Q8_RDYTIMECFG 0x0920 +#define AR_Q9_RDYTIMECFG 0x0924 +#define AR_QRDYTIMECFG(_i) (AR_Q0_RDYTIMECFG + ((_i)<<2)) +#define AR_Q_RDYTIMECFG_DURATION 0x00FFFFFF +#define AR_Q_RDYTIMECFG_DURATION_S 0 +#define AR_Q_RDYTIMECFG_EN 0x01000000 + +#define AR_Q_ONESHOTARM_SC 0x0940 +#define AR_Q_ONESHOTARM_SC_M 0x000003FF +#define AR_Q_ONESHOTARM_SC_RESV0 0xFFFFFC00 + +#define AR_Q_ONESHOTARM_CC 0x0980 +#define AR_Q_ONESHOTARM_CC_M 0x000003FF +#define AR_Q_ONESHOTARM_CC_RESV0 0xFFFFFC00 + +#define AR_Q0_MISC 0x09c0 +#define AR_Q1_MISC 0x09c4 +#define AR_Q2_MISC 0x09c8 +#define AR_Q3_MISC 0x09cc +#define AR_Q4_MISC 0x09d0 +#define AR_Q5_MISC 0x09d4 +#define AR_Q6_MISC 0x09d8 +#define AR_Q7_MISC 0x09dc +#define AR_Q8_MISC 0x09e0 +#define AR_Q9_MISC 0x09e4 +#define AR_QMISC(_i) (AR_Q0_MISC + ((_i)<<2)) +#define AR_Q_MISC_FSP 0x0000000F +#define AR_Q_MISC_FSP_ASAP 0 +#define AR_Q_MISC_FSP_CBR 1 +#define AR_Q_MISC_FSP_DBA_GATED 2 +#define AR_Q_MISC_FSP_TIM_GATED 3 +#define AR_Q_MISC_FSP_BEACON_SENT_GATED 4 +#define AR_Q_MISC_FSP_BEACON_RCVD_GATED 5 +#define AR_Q_MISC_ONE_SHOT_EN 0x00000010 +#define AR_Q_MISC_CBR_INCR_DIS1 0x00000020 +#define AR_Q_MISC_CBR_INCR_DIS0 0x00000040 +#define AR_Q_MISC_BEACON_USE 0x00000080 +#define AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN 0x00000100 +#define AR_Q_MISC_RDYTIME_EXP_POLICY 0x00000200 +#define AR_Q_MISC_RESET_CBR_EXP_CTR 0x00000400 +#define AR_Q_MISC_DCU_EARLY_TERM_REQ 0x00000800 +#define AR_Q_MISC_RESV0 0xFFFFF000 + +#define AR_Q0_STS 0x0a00 +#define AR_Q1_STS 0x0a04 +#define AR_Q2_STS 0x0a08 +#define AR_Q3_STS 0x0a0c +#define AR_Q4_STS 0x0a10 +#define AR_Q5_STS 0x0a14 +#define AR_Q6_STS 0x0a18 +#define AR_Q7_STS 0x0a1c +#define AR_Q8_STS 0x0a20 +#define AR_Q9_STS 0x0a24 +#define AR_QSTS(_i) (AR_Q0_STS + ((_i)<<2)) +#define AR_Q_STS_PEND_FR_CNT 0x00000003 +#define AR_Q_STS_RESV0 0x000000FC +#define AR_Q_STS_CBR_EXP_CNT 0x0000FF00 +#define AR_Q_STS_RESV1 0xFFFF0000 + +#define AR_Q_RDYTIMESHDN 0x0a40 +#define AR_Q_RDYTIMESHDN_M 0x000003FF + + +#define AR_NUM_DCU 10 +#define AR_DCU_0 0x0001 +#define AR_DCU_1 0x0002 +#define AR_DCU_2 0x0004 +#define AR_DCU_3 0x0008 +#define AR_DCU_4 0x0010 +#define AR_DCU_5 0x0020 +#define AR_DCU_6 0x0040 +#define AR_DCU_7 0x0080 +#define AR_DCU_8 0x0100 +#define AR_DCU_9 0x0200 + +#define AR_D0_QCUMASK 0x1000 +#define AR_D1_QCUMASK 0x1004 +#define AR_D2_QCUMASK 0x1008 +#define AR_D3_QCUMASK 0x100c +#define AR_D4_QCUMASK 0x1010 +#define AR_D5_QCUMASK 0x1014 +#define AR_D6_QCUMASK 0x1018 +#define AR_D7_QCUMASK 0x101c +#define AR_D8_QCUMASK 0x1020 +#define AR_D9_QCUMASK 0x1024 +#define AR_DQCUMASK(_i) (AR_D0_QCUMASK + ((_i)<<2)) +#define AR_D_QCUMASK 0x000003FF +#define AR_D_QCUMASK_RESV0 0xFFFFFC00 + +#define AR_D_TXBLK_CMD 0x1038 +#define AR_D_TXBLK_DATA(i) (AR_D_TXBLK_CMD+(i)) + +#define AR_D0_LCL_IFS 0x1040 +#define AR_D1_LCL_IFS 0x1044 +#define AR_D2_LCL_IFS 0x1048 +#define AR_D3_LCL_IFS 0x104c +#define AR_D4_LCL_IFS 0x1050 +#define AR_D5_LCL_IFS 0x1054 +#define AR_D6_LCL_IFS 0x1058 +#define AR_D7_LCL_IFS 0x105c +#define AR_D8_LCL_IFS 0x1060 +#define AR_D9_LCL_IFS 0x1064 +#define AR_DLCL_IFS(_i) (AR_D0_LCL_IFS + ((_i)<<2)) +#define AR_D_LCL_IFS_CWMIN 0x000003FF +#define AR_D_LCL_IFS_CWMIN_S 0 +#define AR_D_LCL_IFS_CWMAX 0x000FFC00 +#define AR_D_LCL_IFS_CWMAX_S 10 +#define AR_D_LCL_IFS_AIFS 0x0FF00000 +#define AR_D_LCL_IFS_AIFS_S 20 + +#define AR_D_LCL_IFS_RESV0 0xF0000000 + +#define AR_D0_RETRY_LIMIT 0x1080 +#define AR_D1_RETRY_LIMIT 0x1084 +#define AR_D2_RETRY_LIMIT 0x1088 +#define AR_D3_RETRY_LIMIT 0x108c +#define AR_D4_RETRY_LIMIT 0x1090 +#define AR_D5_RETRY_LIMIT 0x1094 +#define AR_D6_RETRY_LIMIT 0x1098 +#define AR_D7_RETRY_LIMIT 0x109c +#define AR_D8_RETRY_LIMIT 0x10a0 +#define AR_D9_RETRY_LIMIT 0x10a4 +#define AR_DRETRY_LIMIT(_i) (AR_D0_RETRY_LIMIT + ((_i)<<2)) +#define AR_D_RETRY_LIMIT_FR_SH 0x0000000F +#define AR_D_RETRY_LIMIT_FR_SH_S 0 +#define AR_D_RETRY_LIMIT_STA_SH 0x00003F00 +#define AR_D_RETRY_LIMIT_STA_SH_S 8 +#define AR_D_RETRY_LIMIT_STA_LG 0x000FC000 +#define AR_D_RETRY_LIMIT_STA_LG_S 14 +#define AR_D_RETRY_LIMIT_RESV0 0xFFF00000 + +#define AR_D0_CHNTIME 0x10c0 +#define AR_D1_CHNTIME 0x10c4 +#define AR_D2_CHNTIME 0x10c8 +#define AR_D3_CHNTIME 0x10cc +#define AR_D4_CHNTIME 0x10d0 +#define AR_D5_CHNTIME 0x10d4 +#define AR_D6_CHNTIME 0x10d8 +#define AR_D7_CHNTIME 0x10dc +#define AR_D8_CHNTIME 0x10e0 +#define AR_D9_CHNTIME 0x10e4 +#define AR_DCHNTIME(_i) (AR_D0_CHNTIME + ((_i)<<2)) +#define AR_D_CHNTIME_DUR 0x000FFFFF +#define AR_D_CHNTIME_DUR_S 0 +#define AR_D_CHNTIME_EN 0x00100000 +#define AR_D_CHNTIME_RESV0 0xFFE00000 + +#define AR_D0_MISC 0x1100 +#define AR_D1_MISC 0x1104 +#define AR_D2_MISC 0x1108 +#define AR_D3_MISC 0x110c +#define AR_D4_MISC 0x1110 +#define AR_D5_MISC 0x1114 +#define AR_D6_MISC 0x1118 +#define AR_D7_MISC 0x111c +#define AR_D8_MISC 0x1120 +#define AR_D9_MISC 0x1124 +#define AR_DMISC(_i) (AR_D0_MISC + ((_i)<<2)) +#define AR_D_MISC_BKOFF_THRESH 0x0000003F +#define AR_D_MISC_RETRY_CNT_RESET_EN 0x00000040 +#define AR_D_MISC_CW_RESET_EN 0x00000080 +#define AR_D_MISC_FRAG_WAIT_EN 0x00000100 +#define AR_D_MISC_FRAG_BKOFF_EN 0x00000200 +#define AR_D_MISC_CW_BKOFF_EN 0x00001000 +#define AR_D_MISC_VIR_COL_HANDLING 0x0000C000 +#define AR_D_MISC_VIR_COL_HANDLING_S 14 +#define AR_D_MISC_VIR_COL_HANDLING_DEFAULT 0 +#define AR_D_MISC_VIR_COL_HANDLING_IGNORE 1 +#define AR_D_MISC_BEACON_USE 0x00010000 +#define AR_D_MISC_ARB_LOCKOUT_CNTRL 0x00060000 +#define AR_D_MISC_ARB_LOCKOUT_CNTRL_S 17 +#define AR_D_MISC_ARB_LOCKOUT_CNTRL_NONE 0 +#define AR_D_MISC_ARB_LOCKOUT_CNTRL_INTRA_FR 1 +#define AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL 2 +#define AR_D_MISC_ARB_LOCKOUT_IGNORE 0x00080000 +#define AR_D_MISC_SEQ_NUM_INCR_DIS 0x00100000 +#define AR_D_MISC_POST_FR_BKOFF_DIS 0x00200000 +#define AR_D_MISC_VIT_COL_CW_BKOFF_EN 0x00400000 +#define AR_D_MISC_BLOWN_IFS_RETRY_EN 0x00800000 +#define AR_D_MISC_RESV0 0xFF000000 + +#define AR_D_SEQNUM 0x1140 + +#define AR_D_GBL_IFS_SIFS 0x1030 +#define AR_D_GBL_IFS_SIFS_M 0x0000FFFF +#define AR_D_GBL_IFS_SIFS_RESV0 0xFFFFFFFF + +#define AR_D_TXBLK_BASE 0x1038 +#define AR_D_TXBLK_WRITE_BITMASK 0x0000FFFF +#define AR_D_TXBLK_WRITE_BITMASK_S 0 +#define AR_D_TXBLK_WRITE_SLICE 0x000F0000 +#define AR_D_TXBLK_WRITE_SLICE_S 16 +#define AR_D_TXBLK_WRITE_DCU 0x00F00000 +#define AR_D_TXBLK_WRITE_DCU_S 20 +#define AR_D_TXBLK_WRITE_COMMAND 0x0F000000 +#define AR_D_TXBLK_WRITE_COMMAND_S 24 + +#define AR_D_GBL_IFS_SLOT 0x1070 +#define AR_D_GBL_IFS_SLOT_M 0x0000FFFF +#define AR_D_GBL_IFS_SLOT_RESV0 0xFFFF0000 + +#define AR_D_GBL_IFS_EIFS 0x10b0 +#define AR_D_GBL_IFS_EIFS_M 0x0000FFFF +#define AR_D_GBL_IFS_EIFS_RESV0 0xFFFF0000 + +#define AR_D_GBL_IFS_MISC 0x10f0 +#define AR_D_GBL_IFS_MISC_LFSR_SLICE_SEL 0x00000007 +#define AR_D_GBL_IFS_MISC_TURBO_MODE 0x00000008 +#define AR_D_GBL_IFS_MISC_USEC_DURATION 0x000FFC00 +#define AR_D_GBL_IFS_MISC_DCU_ARBITER_DLY 0x00300000 +#define AR_D_GBL_IFS_MISC_RANDOM_LFSR_SLICE_DIS 0x01000000 +#define AR_D_GBL_IFS_MISC_SLOT_XMIT_WIND_LEN 0x06000000 +#define AR_D_GBL_IFS_MISC_FORCE_XMIT_SLOT_BOUND 0x08000000 +#define AR_D_GBL_IFS_MISC_IGNORE_BACKOFF 0x10000000 + +#define AR_D_FPCTL 0x1230 +#define AR_D_FPCTL_DCU 0x0000000F +#define AR_D_FPCTL_DCU_S 0 +#define AR_D_FPCTL_PREFETCH_EN 0x00000010 +#define AR_D_FPCTL_BURST_PREFETCH 0x00007FE0 +#define AR_D_FPCTL_BURST_PREFETCH_S 5 + +#define AR_D_TXPSE 0x1270 +#define AR_D_TXPSE_CTRL 0x000003FF +#define AR_D_TXPSE_RESV0 0x0000FC00 +#define AR_D_TXPSE_STATUS 0x00010000 +#define AR_D_TXPSE_RESV1 0xFFFE0000 + +#define AR_D_TXSLOTMASK 0x12f0 +#define AR_D_TXSLOTMASK_NUM 0x0000000F + +#define AR_CFG_LED 0x1f04 +#define AR_CFG_SCLK_RATE_IND 0x00000003 +#define AR_CFG_SCLK_RATE_IND_S 0 +#define AR_CFG_SCLK_32MHZ 0x00000000 +#define AR_CFG_SCLK_4MHZ 0x00000001 +#define AR_CFG_SCLK_1MHZ 0x00000002 +#define AR_CFG_SCLK_32KHZ 0x00000003 +#define AR_CFG_LED_BLINK_SLOW 0x00000008 +#define AR_CFG_LED_BLINK_THRESH_SEL 0x00000070 +#define AR_CFG_LED_MODE_SEL 0x00000380 +#define AR_CFG_LED_MODE_SEL_S 7 +#define AR_CFG_LED_POWER 0x00000280 +#define AR_CFG_LED_POWER_S 7 +#define AR_CFG_LED_NETWORK 0x00000300 +#define AR_CFG_LED_NETWORK_S 7 +#define AR_CFG_LED_MODE_PROP 0x0 +#define AR_CFG_LED_MODE_RPROP 0x1 +#define AR_CFG_LED_MODE_SPLIT 0x2 +#define AR_CFG_LED_MODE_RAND 0x3 +#define AR_CFG_LED_MODE_POWER_OFF 0x4 +#define AR_CFG_LED_MODE_POWER_ON 0x5 +#define AR_CFG_LED_MODE_NETWORK_OFF 0x4 +#define AR_CFG_LED_MODE_NETWORK_ON 0x6 +#define AR_CFG_LED_ASSOC_CTL 0x00000c00 +#define AR_CFG_LED_ASSOC_CTL_S 10 +#define AR_CFG_LED_ASSOC_NONE 0x0 +#define AR_CFG_LED_ASSOC_ACTIVE 0x1 +#define AR_CFG_LED_ASSOC_PENDING 0x2 + +#define AR_CFG_LED_BLINK_SLOW 0x00000008 +#define AR_CFG_LED_BLINK_SLOW_S 3 + +#define AR_CFG_LED_BLINK_THRESH_SEL 0x00000070 +#define AR_CFG_LED_BLINK_THRESH_SEL_S 4 + +#define AR_MAC_SLEEP 0x1f00 +#define AR_MAC_SLEEP_MAC_AWAKE 0x00000000 +#define AR_MAC_SLEEP_MAC_ASLEEP 0x00000001 + +#define AR_RC 0x4000 +#define AR_RC_AHB 0x00000001 +#define AR_RC_APB 0x00000002 +#define AR_RC_HOSTIF 0x00000100 + +#define AR_WA 0x4004 +#define AR9285_WA_DEFAULT 0x004a05cb +#define AR9280_WA_DEFAULT 0x0040073f +#define AR_WA_DEFAULT 0x0000073f + + +#define AR_PM_STATE 0x4008 +#define AR_PM_STATE_PME_D3COLD_VAUX 0x00100000 + +#define AR_HOST_TIMEOUT 0x4018 +#define AR_HOST_TIMEOUT_APB_CNTR 0x0000FFFF +#define AR_HOST_TIMEOUT_APB_CNTR_S 0 +#define AR_HOST_TIMEOUT_LCL_CNTR 0xFFFF0000 +#define AR_HOST_TIMEOUT_LCL_CNTR_S 16 + +#define AR_EEPROM 0x401c +#define AR_EEPROM_ABSENT 0x00000100 +#define AR_EEPROM_CORRUPT 0x00000200 +#define AR_EEPROM_PROT_MASK 0x03FFFC00 +#define AR_EEPROM_PROT_MASK_S 10 + +#define EEPROM_PROTECT_RP_0_31 0x0001 +#define EEPROM_PROTECT_WP_0_31 0x0002 +#define EEPROM_PROTECT_RP_32_63 0x0004 +#define EEPROM_PROTECT_WP_32_63 0x0008 +#define EEPROM_PROTECT_RP_64_127 0x0010 +#define EEPROM_PROTECT_WP_64_127 0x0020 +#define EEPROM_PROTECT_RP_128_191 0x0040 +#define EEPROM_PROTECT_WP_128_191 0x0080 +#define EEPROM_PROTECT_RP_192_255 0x0100 +#define EEPROM_PROTECT_WP_192_255 0x0200 +#define EEPROM_PROTECT_RP_256_511 0x0400 +#define EEPROM_PROTECT_WP_256_511 0x0800 +#define EEPROM_PROTECT_RP_512_1023 0x1000 +#define EEPROM_PROTECT_WP_512_1023 0x2000 +#define EEPROM_PROTECT_RP_1024_2047 0x4000 +#define EEPROM_PROTECT_WP_1024_2047 0x8000 + +#define AR_SREV \ + ((AR_SREV_9100(ah)) ? 0x0600 : 0x4020) + +#define AR_SREV_ID \ + ((AR_SREV_9100(ah)) ? 0x00000FFF : 0x000000FF) +#define AR_SREV_VERSION 0x000000F0 +#define AR_SREV_VERSION_S 4 +#define AR_SREV_REVISION 0x00000007 + +#define AR_SREV_ID2 0xFFFFFFFF +#define AR_SREV_VERSION2 0xFFFC0000 +#define AR_SREV_VERSION2_S 18 +#define AR_SREV_TYPE2 0x0003F000 +#define AR_SREV_TYPE2_S 12 +#define AR_SREV_TYPE2_CHAIN 0x00001000 +#define AR_SREV_TYPE2_HOST_MODE 0x00002000 +#define AR_SREV_REVISION2 0x00000F00 +#define AR_SREV_REVISION2_S 8 + +#define AR_SREV_VERSION_5416_PCI 0xD +#define AR_SREV_VERSION_5416_PCIE 0xC +#define AR_SREV_REVISION_5416_10 0 +#define AR_SREV_REVISION_5416_20 1 +#define AR_SREV_REVISION_5416_22 2 +#define AR_SREV_VERSION_9100 0x14 +#define AR_SREV_VERSION_9160 0x40 +#define AR_SREV_REVISION_9160_10 0 +#define AR_SREV_REVISION_9160_11 1 +#define AR_SREV_VERSION_9280 0x80 +#define AR_SREV_REVISION_9280_10 0 +#define AR_SREV_REVISION_9280_20 1 +#define AR_SREV_REVISION_9280_21 2 +#define AR_SREV_VERSION_9285 0xC0 +#define AR_SREV_REVISION_9285_10 0 +#define AR_SREV_REVISION_9285_11 1 +#define AR_SREV_REVISION_9285_12 2 + +#define AR_SREV_5416(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \ + ((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE)) +#define AR_SREV_5416_20_OR_LATER(_ah) \ + (((AR_SREV_5416(_ah)) && \ + ((_ah)->hw_version.macRev >= AR_SREV_REVISION_5416_20)) || \ + ((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9100)) +#define AR_SREV_5416_22_OR_LATER(_ah) \ + (((AR_SREV_5416(_ah)) && \ + ((_ah)->hw_version.macRev >= AR_SREV_REVISION_5416_22)) || \ + ((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9100)) + +#define AR_SREV_9100(ah) \ + ((ah->hw_version.macVersion) == AR_SREV_VERSION_9100) +#define AR_SREV_9100_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9100)) + +#define AR_SREV_9160(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9160)) +#define AR_SREV_9160_10_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9160)) +#define AR_SREV_9160_11(_ah) \ + (AR_SREV_9160(_ah) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9160_11)) +#define AR_SREV_9280(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9280)) +#define AR_SREV_9280_10_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9280)) +#define AR_SREV_9280_20(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9280) && \ + ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9280_20)) +#define AR_SREV_9280_20_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9280) || \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9280) && \ + ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9280_20))) + +#define AR_SREV_9285(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9285)) +#define AR_SREV_9285_10_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9285)) +#define AR_SREV_9285_11(_ah) \ + (AR_SREV_9285(ah) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9285_11)) +#define AR_SREV_9285_11_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9285) || \ + (AR_SREV_9285(ah) && ((_ah)->hw_version.macRev >= \ + AR_SREV_REVISION_9285_11))) +#define AR_SREV_9285_12(_ah) \ + (AR_SREV_9285(ah) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9285_12)) +#define AR_SREV_9285_12_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9285) || \ + (AR_SREV_9285(ah) && ((_ah)->hw_version.macRev >= \ + AR_SREV_REVISION_9285_12))) + +#define AR_RADIO_SREV_MAJOR 0xf0 +#define AR_RAD5133_SREV_MAJOR 0xc0 +#define AR_RAD2133_SREV_MAJOR 0xd0 +#define AR_RAD5122_SREV_MAJOR 0xe0 +#define AR_RAD2122_SREV_MAJOR 0xf0 + +#define AR_AHB_MODE 0x4024 +#define AR_AHB_EXACT_WR_EN 0x00000000 +#define AR_AHB_BUF_WR_EN 0x00000001 +#define AR_AHB_EXACT_RD_EN 0x00000000 +#define AR_AHB_CACHELINE_RD_EN 0x00000002 +#define AR_AHB_PREFETCH_RD_EN 0x00000004 +#define AR_AHB_PAGE_SIZE_1K 0x00000000 +#define AR_AHB_PAGE_SIZE_2K 0x00000008 +#define AR_AHB_PAGE_SIZE_4K 0x00000010 + +#define AR_INTR_RTC_IRQ 0x00000001 +#define AR_INTR_MAC_IRQ 0x00000002 +#define AR_INTR_EEP_PROT_ACCESS 0x00000004 +#define AR_INTR_MAC_AWAKE 0x00020000 +#define AR_INTR_MAC_ASLEEP 0x00040000 +#define AR_INTR_SPURIOUS 0xFFFFFFFF + + +#define AR_INTR_SYNC_CAUSE_CLR 0x4028 + +#define AR_INTR_SYNC_CAUSE 0x4028 + +#define AR_INTR_SYNC_ENABLE 0x402c +#define AR_INTR_SYNC_ENABLE_GPIO 0xFFFC0000 +#define AR_INTR_SYNC_ENABLE_GPIO_S 18 + +enum { + AR_INTR_SYNC_RTC_IRQ = 0x00000001, + AR_INTR_SYNC_MAC_IRQ = 0x00000002, + AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS = 0x00000004, + AR_INTR_SYNC_APB_TIMEOUT = 0x00000008, + AR_INTR_SYNC_PCI_MODE_CONFLICT = 0x00000010, + AR_INTR_SYNC_HOST1_FATAL = 0x00000020, + AR_INTR_SYNC_HOST1_PERR = 0x00000040, + AR_INTR_SYNC_TRCV_FIFO_PERR = 0x00000080, + AR_INTR_SYNC_RADM_CPL_EP = 0x00000100, + AR_INTR_SYNC_RADM_CPL_DLLP_ABORT = 0x00000200, + AR_INTR_SYNC_RADM_CPL_TLP_ABORT = 0x00000400, + AR_INTR_SYNC_RADM_CPL_ECRC_ERR = 0x00000800, + AR_INTR_SYNC_RADM_CPL_TIMEOUT = 0x00001000, + AR_INTR_SYNC_LOCAL_TIMEOUT = 0x00002000, + AR_INTR_SYNC_PM_ACCESS = 0x00004000, + AR_INTR_SYNC_MAC_AWAKE = 0x00008000, + AR_INTR_SYNC_MAC_ASLEEP = 0x00010000, + AR_INTR_SYNC_MAC_SLEEP_ACCESS = 0x00020000, + AR_INTR_SYNC_ALL = 0x0003FFFF, + + + AR_INTR_SYNC_DEFAULT = (AR_INTR_SYNC_HOST1_FATAL | + AR_INTR_SYNC_HOST1_PERR | + AR_INTR_SYNC_RADM_CPL_EP | + AR_INTR_SYNC_RADM_CPL_DLLP_ABORT | + AR_INTR_SYNC_RADM_CPL_TLP_ABORT | + AR_INTR_SYNC_RADM_CPL_ECRC_ERR | + AR_INTR_SYNC_RADM_CPL_TIMEOUT | + AR_INTR_SYNC_LOCAL_TIMEOUT | + AR_INTR_SYNC_MAC_SLEEP_ACCESS), + + AR_INTR_SYNC_SPURIOUS = 0xFFFFFFFF, + +}; + +#define AR_INTR_ASYNC_MASK 0x4030 +#define AR_INTR_ASYNC_MASK_GPIO 0xFFFC0000 +#define AR_INTR_ASYNC_MASK_GPIO_S 18 + +#define AR_INTR_SYNC_MASK 0x4034 +#define AR_INTR_SYNC_MASK_GPIO 0xFFFC0000 +#define AR_INTR_SYNC_MASK_GPIO_S 18 + +#define AR_INTR_ASYNC_CAUSE_CLR 0x4038 +#define AR_INTR_ASYNC_CAUSE 0x4038 + +#define AR_INTR_ASYNC_ENABLE 0x403c +#define AR_INTR_ASYNC_ENABLE_GPIO 0xFFFC0000 +#define AR_INTR_ASYNC_ENABLE_GPIO_S 18 + +#define AR_PCIE_SERDES 0x4040 +#define AR_PCIE_SERDES2 0x4044 +#define AR_PCIE_PM_CTRL 0x4014 +#define AR_PCIE_PM_CTRL_ENA 0x00080000 + +#define AR_NUM_GPIO 14 +#define AR928X_NUM_GPIO 10 +#define AR9285_NUM_GPIO 12 + +#define AR_GPIO_IN_OUT 0x4048 +#define AR_GPIO_IN_VAL 0x0FFFC000 +#define AR_GPIO_IN_VAL_S 14 +#define AR928X_GPIO_IN_VAL 0x000FFC00 +#define AR928X_GPIO_IN_VAL_S 10 +#define AR9285_GPIO_IN_VAL 0x00FFF000 +#define AR9285_GPIO_IN_VAL_S 12 + +#define AR_GPIO_OE_OUT 0x404c +#define AR_GPIO_OE_OUT_DRV 0x3 +#define AR_GPIO_OE_OUT_DRV_NO 0x0 +#define AR_GPIO_OE_OUT_DRV_LOW 0x1 +#define AR_GPIO_OE_OUT_DRV_HI 0x2 +#define AR_GPIO_OE_OUT_DRV_ALL 0x3 + +#define AR_GPIO_INTR_POL 0x4050 +#define AR_GPIO_INTR_POL_VAL 0x00001FFF +#define AR_GPIO_INTR_POL_VAL_S 0 + +#define AR_GPIO_INPUT_EN_VAL 0x4054 +#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF 0x00000004 +#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_S 2 +#define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF 0x00000008 +#define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_S 3 +#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_DEF 0x00000010 +#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_S 4 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF 0x00000080 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S 7 +#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB 0x00001000 +#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB_S 12 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB 0x00008000 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB_S 15 +#define AR_GPIO_RTC_RESET_OVERRIDE_ENABLE 0x00010000 +#define AR_GPIO_JTAG_DISABLE 0x00020000 + +#define AR_GPIO_INPUT_MUX1 0x4058 +#define AR_GPIO_INPUT_MUX1_BT_ACTIVE 0x000f0000 +#define AR_GPIO_INPUT_MUX1_BT_ACTIVE_S 16 + +#define AR_GPIO_INPUT_MUX2 0x405c +#define AR_GPIO_INPUT_MUX2_CLK25 0x0000000f +#define AR_GPIO_INPUT_MUX2_CLK25_S 0 +#define AR_GPIO_INPUT_MUX2_RFSILENT 0x000000f0 +#define AR_GPIO_INPUT_MUX2_RFSILENT_S 4 +#define AR_GPIO_INPUT_MUX2_RTC_RESET 0x00000f00 +#define AR_GPIO_INPUT_MUX2_RTC_RESET_S 8 + +#define AR_GPIO_OUTPUT_MUX1 0x4060 +#define AR_GPIO_OUTPUT_MUX2 0x4064 +#define AR_GPIO_OUTPUT_MUX3 0x4068 + +#define AR_INPUT_STATE 0x406c + +#define AR_EEPROM_STATUS_DATA 0x407c +#define AR_EEPROM_STATUS_DATA_VAL 0x0000ffff +#define AR_EEPROM_STATUS_DATA_VAL_S 0 +#define AR_EEPROM_STATUS_DATA_BUSY 0x00010000 +#define AR_EEPROM_STATUS_DATA_BUSY_ACCESS 0x00020000 +#define AR_EEPROM_STATUS_DATA_PROT_ACCESS 0x00040000 +#define AR_EEPROM_STATUS_DATA_ABSENT_ACCESS 0x00080000 + +#define AR_OBS 0x4080 + +#define AR_PCIE_MSI 0x4094 +#define AR_PCIE_MSI_ENABLE 0x00000001 + + +#define AR_RTC_9160_PLL_DIV 0x000003ff +#define AR_RTC_9160_PLL_DIV_S 0 +#define AR_RTC_9160_PLL_REFDIV 0x00003C00 +#define AR_RTC_9160_PLL_REFDIV_S 10 +#define AR_RTC_9160_PLL_CLKSEL 0x0000C000 +#define AR_RTC_9160_PLL_CLKSEL_S 14 + +#define AR_RTC_BASE 0x00020000 +#define AR_RTC_RC \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0000) : 0x7000) +#define AR_RTC_RC_M 0x00000003 +#define AR_RTC_RC_MAC_WARM 0x00000001 +#define AR_RTC_RC_MAC_COLD 0x00000002 +#define AR_RTC_RC_COLD_RESET 0x00000004 +#define AR_RTC_RC_WARM_RESET 0x00000008 + +#define AR_RTC_PLL_CONTROL \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0014) : 0x7014) + +#define AR_RTC_PLL_DIV 0x0000001f +#define AR_RTC_PLL_DIV_S 0 +#define AR_RTC_PLL_DIV2 0x00000020 +#define AR_RTC_PLL_REFDIV_5 0x000000c0 +#define AR_RTC_PLL_CLKSEL 0x00000300 +#define AR_RTC_PLL_CLKSEL_S 8 + +#define AR_RTC_RESET \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0040) : 0x7040) +#define AR_RTC_RESET_EN (0x00000001) + +#define AR_RTC_STATUS \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0044) : 0x7044) + +#define AR_RTC_STATUS_M \ + ((AR_SREV_9100(ah)) ? 0x0000003f : 0x0000000f) + +#define AR_RTC_PM_STATUS_M 0x0000000f + +#define AR_RTC_STATUS_SHUTDOWN 0x00000001 +#define AR_RTC_STATUS_ON 0x00000002 +#define AR_RTC_STATUS_SLEEP 0x00000004 +#define AR_RTC_STATUS_WAKEUP 0x00000008 + +#define AR_RTC_SLEEP_CLK \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0048) : 0x7048) +#define AR_RTC_FORCE_DERIVED_CLK 0x2 + +#define AR_RTC_FORCE_WAKE \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x004c) : 0x704c) +#define AR_RTC_FORCE_WAKE_EN 0x00000001 +#define AR_RTC_FORCE_WAKE_ON_INT 0x00000002 + + +#define AR_RTC_INTR_CAUSE \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0050) : 0x7050) + +#define AR_RTC_INTR_ENABLE \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0054) : 0x7054) + +#define AR_RTC_INTR_MASK \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0058) : 0x7058) + +/* RTC_DERIVED_* - only for AR9100 */ + +#define AR_RTC_DERIVED_CLK (AR_RTC_BASE + 0x0038) +#define AR_RTC_DERIVED_CLK_PERIOD 0x0000fffe +#define AR_RTC_DERIVED_CLK_PERIOD_S 1 + +#define AR_SEQ_MASK 0x8060 + +#define AR_AN_RF2G1_CH0 0x7810 +#define AR_AN_RF2G1_CH0_OB 0x03800000 +#define AR_AN_RF2G1_CH0_OB_S 23 +#define AR_AN_RF2G1_CH0_DB 0x1C000000 +#define AR_AN_RF2G1_CH0_DB_S 26 + +#define AR_AN_RF5G1_CH0 0x7818 +#define AR_AN_RF5G1_CH0_OB5 0x00070000 +#define AR_AN_RF5G1_CH0_OB5_S 16 +#define AR_AN_RF5G1_CH0_DB5 0x00380000 +#define AR_AN_RF5G1_CH0_DB5_S 19 + +#define AR_AN_RF2G1_CH1 0x7834 +#define AR_AN_RF2G1_CH1_OB 0x03800000 +#define AR_AN_RF2G1_CH1_OB_S 23 +#define AR_AN_RF2G1_CH1_DB 0x1C000000 +#define AR_AN_RF2G1_CH1_DB_S 26 + +#define AR_AN_RF5G1_CH1 0x783C +#define AR_AN_RF5G1_CH1_OB5 0x00070000 +#define AR_AN_RF5G1_CH1_OB5_S 16 +#define AR_AN_RF5G1_CH1_DB5 0x00380000 +#define AR_AN_RF5G1_CH1_DB5_S 19 + +#define AR_AN_TOP1 0x7890 +#define AR_AN_TOP1_DACIPMODE 0x00040000 +#define AR_AN_TOP1_DACIPMODE_S 18 + +#define AR_AN_TOP2 0x7894 +#define AR_AN_TOP2_XPABIAS_LVL 0xC0000000 +#define AR_AN_TOP2_XPABIAS_LVL_S 30 +#define AR_AN_TOP2_LOCALBIAS 0x00200000 +#define AR_AN_TOP2_LOCALBIAS_S 21 +#define AR_AN_TOP2_PWDCLKIND 0x00400000 +#define AR_AN_TOP2_PWDCLKIND_S 22 + +#define AR_AN_SYNTH9 0x7868 +#define AR_AN_SYNTH9_REFDIVA 0xf8000000 +#define AR_AN_SYNTH9_REFDIVA_S 27 + +#define AR9285_AN_RF2G1 0x7820 +#define AR9285_AN_RF2G1_ENPACAL 0x00000800 +#define AR9285_AN_RF2G1_ENPACAL_S 11 +#define AR9285_AN_RF2G1_PDPADRV1 0x02000000 +#define AR9285_AN_RF2G1_PDPADRV1_S 25 +#define AR9285_AN_RF2G1_PDPADRV2 0x01000000 +#define AR9285_AN_RF2G1_PDPADRV2_S 24 +#define AR9285_AN_RF2G1_PDPAOUT 0x00800000 +#define AR9285_AN_RF2G1_PDPAOUT_S 23 + + +#define AR9285_AN_RF2G2 0x7824 +#define AR9285_AN_RF2G2_OFFCAL 0x00001000 +#define AR9285_AN_RF2G2_OFFCAL_S 12 + +#define AR9285_AN_RF2G3 0x7828 +#define AR9285_AN_RF2G3_PDVCCOMP 0x02000000 +#define AR9285_AN_RF2G3_PDVCCOMP_S 25 +#define AR9285_AN_RF2G3_OB_0 0x00E00000 +#define AR9285_AN_RF2G3_OB_0_S 21 +#define AR9285_AN_RF2G3_OB_1 0x001C0000 +#define AR9285_AN_RF2G3_OB_1_S 18 +#define AR9285_AN_RF2G3_OB_2 0x00038000 +#define AR9285_AN_RF2G3_OB_2_S 15 +#define AR9285_AN_RF2G3_OB_3 0x00007000 +#define AR9285_AN_RF2G3_OB_3_S 12 +#define AR9285_AN_RF2G3_OB_4 0x00000E00 +#define AR9285_AN_RF2G3_OB_4_S 9 + +#define AR9285_AN_RF2G3_DB1_0 0x000001C0 +#define AR9285_AN_RF2G3_DB1_0_S 6 +#define AR9285_AN_RF2G3_DB1_1 0x00000038 +#define AR9285_AN_RF2G3_DB1_1_S 3 +#define AR9285_AN_RF2G3_DB1_2 0x00000007 +#define AR9285_AN_RF2G3_DB1_2_S 0 +#define AR9285_AN_RF2G4 0x782C +#define AR9285_AN_RF2G4_DB1_3 0xE0000000 +#define AR9285_AN_RF2G4_DB1_3_S 29 +#define AR9285_AN_RF2G4_DB1_4 0x1C000000 +#define AR9285_AN_RF2G4_DB1_4_S 26 + +#define AR9285_AN_RF2G4_DB2_0 0x03800000 +#define AR9285_AN_RF2G4_DB2_0_S 23 +#define AR9285_AN_RF2G4_DB2_1 0x00700000 +#define AR9285_AN_RF2G4_DB2_1_S 20 +#define AR9285_AN_RF2G4_DB2_2 0x000E0000 +#define AR9285_AN_RF2G4_DB2_2_S 17 +#define AR9285_AN_RF2G4_DB2_3 0x0001C000 +#define AR9285_AN_RF2G4_DB2_3_S 14 +#define AR9285_AN_RF2G4_DB2_4 0x00003800 +#define AR9285_AN_RF2G4_DB2_4_S 11 + +#define AR9285_AN_RF2G6 0x7834 +#define AR9285_AN_RF2G6_CCOMP 0x00007800 +#define AR9285_AN_RF2G6_CCOMP_S 11 +#define AR9285_AN_RF2G6_OFFS 0x03f00000 +#define AR9285_AN_RF2G6_OFFS_S 20 + +#define AR9285_AN_RF2G7 0x7838 +#define AR9285_AN_RF2G7_PWDDB 0x00000002 +#define AR9285_AN_RF2G7_PWDDB_S 1 +#define AR9285_AN_RF2G7_PADRVGN2TAB0 0xE0000000 +#define AR9285_AN_RF2G7_PADRVGN2TAB0_S 29 + +#define AR9285_AN_RF2G8 0x783C +#define AR9285_AN_RF2G8_PADRVGN2TAB0 0x0001C000 +#define AR9285_AN_RF2G8_PADRVGN2TAB0_S 14 + + +#define AR9285_AN_RF2G9 0x7840 +#define AR9285_AN_RXTXBB1 0x7854 +#define AR9285_AN_RXTXBB1_PDRXTXBB1 0x00000020 +#define AR9285_AN_RXTXBB1_PDRXTXBB1_S 5 +#define AR9285_AN_RXTXBB1_PDV2I 0x00000080 +#define AR9285_AN_RXTXBB1_PDV2I_S 7 +#define AR9285_AN_RXTXBB1_PDDACIF 0x00000100 +#define AR9285_AN_RXTXBB1_PDDACIF_S 8 +#define AR9285_AN_RXTXBB1_SPARE9 0x00000001 +#define AR9285_AN_RXTXBB1_SPARE9_S 0 + +#define AR9285_AN_TOP2 0x7868 + +#define AR9285_AN_TOP3 0x786c +#define AR9285_AN_TOP3_XPABIAS_LVL 0x0000000C +#define AR9285_AN_TOP3_XPABIAS_LVL_S 2 +#define AR9285_AN_TOP3_PWDDAC 0x00800000 +#define AR9285_AN_TOP3_PWDDAC_S 23 + +#define AR9285_AN_TOP4 0x7870 +#define AR9285_AN_TOP4_DEFAULT 0x10142c00 + +#define AR_STA_ID0 0x8000 +#define AR_STA_ID1 0x8004 +#define AR_STA_ID1_SADH_MASK 0x0000FFFF +#define AR_STA_ID1_STA_AP 0x00010000 +#define AR_STA_ID1_ADHOC 0x00020000 +#define AR_STA_ID1_PWR_SAV 0x00040000 +#define AR_STA_ID1_KSRCHDIS 0x00080000 +#define AR_STA_ID1_PCF 0x00100000 +#define AR_STA_ID1_USE_DEFANT 0x00200000 +#define AR_STA_ID1_DEFANT_UPDATE 0x00400000 +#define AR_STA_ID1_RTS_USE_DEF 0x00800000 +#define AR_STA_ID1_ACKCTS_6MB 0x01000000 +#define AR_STA_ID1_BASE_RATE_11B 0x02000000 +#define AR_STA_ID1_SECTOR_SELF_GEN 0x04000000 +#define AR_STA_ID1_CRPT_MIC_ENABLE 0x08000000 +#define AR_STA_ID1_KSRCH_MODE 0x10000000 +#define AR_STA_ID1_PRESERVE_SEQNUM 0x20000000 +#define AR_STA_ID1_CBCIV_ENDIAN 0x40000000 +#define AR_STA_ID1_MCAST_KSRCH 0x80000000 + +#define AR_BSS_ID0 0x8008 +#define AR_BSS_ID1 0x800C +#define AR_BSS_ID1_U16 0x0000FFFF +#define AR_BSS_ID1_AID 0x07FF0000 +#define AR_BSS_ID1_AID_S 16 + +#define AR_BCN_RSSI_AVE 0x8010 +#define AR_BCN_RSSI_AVE_MASK 0x00000FFF + +#define AR_TIME_OUT 0x8014 +#define AR_TIME_OUT_ACK 0x00003FFF +#define AR_TIME_OUT_ACK_S 0 +#define AR_TIME_OUT_CTS 0x3FFF0000 +#define AR_TIME_OUT_CTS_S 16 + +#define AR_RSSI_THR 0x8018 +#define AR_RSSI_THR_MASK 0x000000FF +#define AR_RSSI_THR_BM_THR 0x0000FF00 +#define AR_RSSI_THR_BM_THR_S 8 +#define AR_RSSI_BCN_WEIGHT 0x1F000000 +#define AR_RSSI_BCN_WEIGHT_S 24 +#define AR_RSSI_BCN_RSSI_RST 0x20000000 + +#define AR_USEC 0x801c +#define AR_USEC_USEC 0x0000007F +#define AR_USEC_TX_LAT 0x007FC000 +#define AR_USEC_TX_LAT_S 14 +#define AR_USEC_RX_LAT 0x1F800000 +#define AR_USEC_RX_LAT_S 23 + +#define AR_RESET_TSF 0x8020 +#define AR_RESET_TSF_ONCE 0x01000000 + +#define AR_MAX_CFP_DUR 0x8038 +#define AR_CFP_VAL 0x0000FFFF + +#define AR_RX_FILTER 0x803C +#define AR_RX_COMPR_BAR 0x00000400 + +#define AR_MCAST_FIL0 0x8040 +#define AR_MCAST_FIL1 0x8044 + +#define AR_DIAG_SW 0x8048 +#define AR_DIAG_CACHE_ACK 0x00000001 +#define AR_DIAG_ACK_DIS 0x00000002 +#define AR_DIAG_CTS_DIS 0x00000004 +#define AR_DIAG_ENCRYPT_DIS 0x00000008 +#define AR_DIAG_DECRYPT_DIS 0x00000010 +#define AR_DIAG_RX_DIS 0x00000020 +#define AR_DIAG_LOOP_BACK 0x00000040 +#define AR_DIAG_CORR_FCS 0x00000080 +#define AR_DIAG_CHAN_INFO 0x00000100 +#define AR_DIAG_SCRAM_SEED 0x0001FE00 +#define AR_DIAG_SCRAM_SEED_S 8 +#define AR_DIAG_FRAME_NV0 0x00020000 +#define AR_DIAG_OBS_PT_SEL1 0x000C0000 +#define AR_DIAG_OBS_PT_SEL1_S 18 +#define AR_DIAG_FORCE_RX_CLEAR 0x00100000 +#define AR_DIAG_IGNORE_VIRT_CS 0x00200000 +#define AR_DIAG_FORCE_CH_IDLE_HIGH 0x00400000 +#define AR_DIAG_EIFS_CTRL_ENA 0x00800000 +#define AR_DIAG_DUAL_CHAIN_INFO 0x01000000 +#define AR_DIAG_RX_ABORT 0x02000000 +#define AR_DIAG_SATURATE_CYCLE_CNT 0x04000000 +#define AR_DIAG_OBS_PT_SEL2 0x08000000 +#define AR_DIAG_RX_CLEAR_CTL_LOW 0x10000000 +#define AR_DIAG_RX_CLEAR_EXT_LOW 0x20000000 + +#define AR_TSF_L32 0x804c +#define AR_TSF_U32 0x8050 + +#define AR_TST_ADDAC 0x8054 +#define AR_DEF_ANTENNA 0x8058 + +#define AR_AES_MUTE_MASK0 0x805c +#define AR_AES_MUTE_MASK0_FC 0x0000FFFF +#define AR_AES_MUTE_MASK0_QOS 0xFFFF0000 +#define AR_AES_MUTE_MASK0_QOS_S 16 + +#define AR_AES_MUTE_MASK1 0x8060 +#define AR_AES_MUTE_MASK1_SEQ 0x0000FFFF +#define AR_AES_MUTE_MASK1_FC_MGMT 0xFFFF0000 +#define AR_AES_MUTE_MASK1_FC_MGMT_S 16 + +#define AR_GATED_CLKS 0x8064 +#define AR_GATED_CLKS_TX 0x00000002 +#define AR_GATED_CLKS_RX 0x00000004 +#define AR_GATED_CLKS_REG 0x00000008 + +#define AR_OBS_BUS_CTRL 0x8068 +#define AR_OBS_BUS_SEL_1 0x00040000 +#define AR_OBS_BUS_SEL_2 0x00080000 +#define AR_OBS_BUS_SEL_3 0x000C0000 +#define AR_OBS_BUS_SEL_4 0x08040000 +#define AR_OBS_BUS_SEL_5 0x08080000 + +#define AR_OBS_BUS_1 0x806c +#define AR_OBS_BUS_1_PCU 0x00000001 +#define AR_OBS_BUS_1_RX_END 0x00000002 +#define AR_OBS_BUS_1_RX_WEP 0x00000004 +#define AR_OBS_BUS_1_RX_BEACON 0x00000008 +#define AR_OBS_BUS_1_RX_FILTER 0x00000010 +#define AR_OBS_BUS_1_TX_HCF 0x00000020 +#define AR_OBS_BUS_1_QUIET_TIME 0x00000040 +#define AR_OBS_BUS_1_CHAN_IDLE 0x00000080 +#define AR_OBS_BUS_1_TX_HOLD 0x00000100 +#define AR_OBS_BUS_1_TX_FRAME 0x00000200 +#define AR_OBS_BUS_1_RX_FRAME 0x00000400 +#define AR_OBS_BUS_1_RX_CLEAR 0x00000800 +#define AR_OBS_BUS_1_WEP_STATE 0x0003F000 +#define AR_OBS_BUS_1_WEP_STATE_S 12 +#define AR_OBS_BUS_1_RX_STATE 0x01F00000 +#define AR_OBS_BUS_1_RX_STATE_S 20 +#define AR_OBS_BUS_1_TX_STATE 0x7E000000 +#define AR_OBS_BUS_1_TX_STATE_S 25 + +#define AR_LAST_TSTP 0x8080 +#define AR_NAV 0x8084 +#define AR_RTS_OK 0x8088 +#define AR_RTS_FAIL 0x808c +#define AR_ACK_FAIL 0x8090 +#define AR_FCS_FAIL 0x8094 +#define AR_BEACON_CNT 0x8098 + +#define AR_SLEEP1 0x80d4 +#define AR_SLEEP1_ASSUME_DTIM 0x00080000 +#define AR_SLEEP1_CAB_TIMEOUT 0xFFE00000 +#define AR_SLEEP1_CAB_TIMEOUT_S 21 + +#define AR_SLEEP2 0x80d8 +#define AR_SLEEP2_BEACON_TIMEOUT 0xFFE00000 +#define AR_SLEEP2_BEACON_TIMEOUT_S 21 + +#define AR_BSSMSKL 0x80e0 +#define AR_BSSMSKU 0x80e4 + +#define AR_TPC 0x80e8 +#define AR_TPC_ACK 0x0000003f +#define AR_TPC_ACK_S 0x00 +#define AR_TPC_CTS 0x00003f00 +#define AR_TPC_CTS_S 0x08 +#define AR_TPC_CHIRP 0x003f0000 +#define AR_TPC_CHIRP_S 0x16 + +#define AR_TFCNT 0x80ec +#define AR_RFCNT 0x80f0 +#define AR_RCCNT 0x80f4 +#define AR_CCCNT 0x80f8 + +#define AR_QUIET1 0x80fc +#define AR_QUIET1_NEXT_QUIET_S 0 +#define AR_QUIET1_NEXT_QUIET_M 0x0000ffff +#define AR_QUIET1_QUIET_ENABLE 0x00010000 +#define AR_QUIET1_QUIET_ACK_CTS_ENABLE 0x00020000 +#define AR_QUIET2 0x8100 +#define AR_QUIET2_QUIET_PERIOD_S 0 +#define AR_QUIET2_QUIET_PERIOD_M 0x0000ffff +#define AR_QUIET2_QUIET_DUR_S 16 +#define AR_QUIET2_QUIET_DUR 0xffff0000 + +#define AR_TSF_PARM 0x8104 +#define AR_TSF_INCREMENT_M 0x000000ff +#define AR_TSF_INCREMENT_S 0x00 + +#define AR_QOS_NO_ACK 0x8108 +#define AR_QOS_NO_ACK_TWO_BIT 0x0000000f +#define AR_QOS_NO_ACK_TWO_BIT_S 0 +#define AR_QOS_NO_ACK_BIT_OFF 0x00000070 +#define AR_QOS_NO_ACK_BIT_OFF_S 4 +#define AR_QOS_NO_ACK_BYTE_OFF 0x00000180 +#define AR_QOS_NO_ACK_BYTE_OFF_S 7 + +#define AR_PHY_ERR 0x810c + +#define AR_PHY_ERR_DCHIRP 0x00000008 +#define AR_PHY_ERR_RADAR 0x00000020 +#define AR_PHY_ERR_OFDM_TIMING 0x00020000 +#define AR_PHY_ERR_CCK_TIMING 0x02000000 + +#define AR_RXFIFO_CFG 0x8114 + + +#define AR_MIC_QOS_CONTROL 0x8118 +#define AR_MIC_QOS_SELECT 0x811c + +#define AR_PCU_MISC 0x8120 +#define AR_PCU_FORCE_BSSID_MATCH 0x00000001 +#define AR_PCU_MIC_NEW_LOC_ENA 0x00000004 +#define AR_PCU_TX_ADD_TSF 0x00000008 +#define AR_PCU_CCK_SIFS_MODE 0x00000010 +#define AR_PCU_RX_ANT_UPDT 0x00000800 +#define AR_PCU_TXOP_TBTT_LIMIT_ENA 0x00001000 +#define AR_PCU_MISS_BCN_IN_SLEEP 0x00004000 +#define AR_PCU_BUG_12306_FIX_ENA 0x00020000 +#define AR_PCU_FORCE_QUIET_COLL 0x00040000 +#define AR_PCU_TBTT_PROTECT 0x00200000 +#define AR_PCU_CLEAR_VMF 0x01000000 +#define AR_PCU_CLEAR_BA_VALID 0x04000000 + + +#define AR_FILT_OFDM 0x8124 +#define AR_FILT_OFDM_COUNT 0x00FFFFFF + +#define AR_FILT_CCK 0x8128 +#define AR_FILT_CCK_COUNT 0x00FFFFFF + +#define AR_PHY_ERR_1 0x812c +#define AR_PHY_ERR_1_COUNT 0x00FFFFFF +#define AR_PHY_ERR_MASK_1 0x8130 + +#define AR_PHY_ERR_2 0x8134 +#define AR_PHY_ERR_2_COUNT 0x00FFFFFF +#define AR_PHY_ERR_MASK_2 0x8138 + +#define AR_PHY_COUNTMAX (3 << 22) +#define AR_MIBCNT_INTRMASK (3 << 22) + +#define AR_TSFOOR_THRESHOLD 0x813c +#define AR_TSFOOR_THRESHOLD_VAL 0x0000FFFF + +#define AR_PHY_ERR_EIFS_MASK 8144 + +#define AR_PHY_ERR_3 0x8168 +#define AR_PHY_ERR_3_COUNT 0x00FFFFFF +#define AR_PHY_ERR_MASK_3 0x816c + +#define AR_TXSIFS 0x81d0 +#define AR_TXSIFS_TIME 0x000000FF +#define AR_TXSIFS_TX_LATENCY 0x00000F00 +#define AR_TXSIFS_TX_LATENCY_S 8 +#define AR_TXSIFS_ACK_SHIFT 0x00007000 +#define AR_TXSIFS_ACK_SHIFT_S 12 + +#define AR_TXOP_X 0x81ec +#define AR_TXOP_X_VAL 0x000000FF + + +#define AR_TXOP_0_3 0x81f0 +#define AR_TXOP_4_7 0x81f4 +#define AR_TXOP_8_11 0x81f8 +#define AR_TXOP_12_15 0x81fc + + +#define AR_NEXT_TBTT_TIMER 0x8200 +#define AR_NEXT_DMA_BEACON_ALERT 0x8204 +#define AR_NEXT_SWBA 0x8208 +#define AR_NEXT_CFP 0x8208 +#define AR_NEXT_HCF 0x820C +#define AR_NEXT_TIM 0x8210 +#define AR_NEXT_DTIM 0x8214 +#define AR_NEXT_QUIET_TIMER 0x8218 +#define AR_NEXT_NDP_TIMER 0x821C + +#define AR_BEACON_PERIOD 0x8220 +#define AR_DMA_BEACON_PERIOD 0x8224 +#define AR_SWBA_PERIOD 0x8228 +#define AR_HCF_PERIOD 0x822C +#define AR_TIM_PERIOD 0x8230 +#define AR_DTIM_PERIOD 0x8234 +#define AR_QUIET_PERIOD 0x8238 +#define AR_NDP_PERIOD 0x823C + +#define AR_TIMER_MODE 0x8240 +#define AR_TBTT_TIMER_EN 0x00000001 +#define AR_DBA_TIMER_EN 0x00000002 +#define AR_SWBA_TIMER_EN 0x00000004 +#define AR_HCF_TIMER_EN 0x00000008 +#define AR_TIM_TIMER_EN 0x00000010 +#define AR_DTIM_TIMER_EN 0x00000020 +#define AR_QUIET_TIMER_EN 0x00000040 +#define AR_NDP_TIMER_EN 0x00000080 +#define AR_TIMER_OVERFLOW_INDEX 0x00000700 +#define AR_TIMER_OVERFLOW_INDEX_S 8 +#define AR_TIMER_THRESH 0xFFFFF000 +#define AR_TIMER_THRESH_S 12 + +#define AR_SLP32_MODE 0x8244 +#define AR_SLP32_HALF_CLK_LATENCY 0x000FFFFF +#define AR_SLP32_ENA 0x00100000 +#define AR_SLP32_TSF_WRITE_STATUS 0x00200000 + +#define AR_SLP32_WAKE 0x8248 +#define AR_SLP32_WAKE_XTL_TIME 0x0000FFFF + +#define AR_SLP32_INC 0x824c +#define AR_SLP32_TST_INC 0x000FFFFF + +#define AR_SLP_CNT 0x8250 +#define AR_SLP_CYCLE_CNT 0x8254 + +#define AR_SLP_MIB_CTRL 0x8258 +#define AR_SLP_MIB_CLEAR 0x00000001 +#define AR_SLP_MIB_PENDING 0x00000002 + +#define AR_2040_MODE 0x8318 +#define AR_2040_JOINED_RX_CLEAR 0x00000001 + + +#define AR_EXTRCCNT 0x8328 + +#define AR_SELFGEN_MASK 0x832c + +#define AR_PCU_TXBUF_CTRL 0x8340 +#define AR_PCU_TXBUF_CTRL_SIZE_MASK 0x7FF +#define AR_PCU_TXBUF_CTRL_USABLE_SIZE 0x700 +#define AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE 0x380 + +#define AR_PCU_MISC_MODE2 0x8344 +#define AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE 0x00000002 +#define AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT 0x00000004 + +#define AR_KEYTABLE_0 0x8800 +#define AR_KEYTABLE(_n) (AR_KEYTABLE_0 + ((_n)*32)) +#define AR_KEY_CACHE_SIZE 128 +#define AR_RSVD_KEYTABLE_ENTRIES 4 +#define AR_KEY_TYPE 0x00000007 +#define AR_KEYTABLE_TYPE_40 0x00000000 +#define AR_KEYTABLE_TYPE_104 0x00000001 +#define AR_KEYTABLE_TYPE_128 0x00000003 +#define AR_KEYTABLE_TYPE_TKIP 0x00000004 +#define AR_KEYTABLE_TYPE_AES 0x00000005 +#define AR_KEYTABLE_TYPE_CCM 0x00000006 +#define AR_KEYTABLE_TYPE_CLR 0x00000007 +#define AR_KEYTABLE_ANT 0x00000008 +#define AR_KEYTABLE_VALID 0x00008000 +#define AR_KEYTABLE_KEY0(_n) (AR_KEYTABLE(_n) + 0) +#define AR_KEYTABLE_KEY1(_n) (AR_KEYTABLE(_n) + 4) +#define AR_KEYTABLE_KEY2(_n) (AR_KEYTABLE(_n) + 8) +#define AR_KEYTABLE_KEY3(_n) (AR_KEYTABLE(_n) + 12) +#define AR_KEYTABLE_KEY4(_n) (AR_KEYTABLE(_n) + 16) +#define AR_KEYTABLE_TYPE(_n) (AR_KEYTABLE(_n) + 20) +#define AR_KEYTABLE_MAC0(_n) (AR_KEYTABLE(_n) + 24) +#define AR_KEYTABLE_MAC1(_n) (AR_KEYTABLE(_n) + 28) + +#endif diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c new file mode 100644 index 00000000000..1ff429b027d --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/virtual.c @@ -0,0 +1,662 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +struct ath9k_vif_iter_data { + int count; + u8 *addr; +}; + +static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath9k_vif_iter_data *iter_data = data; + u8 *nbuf; + + nbuf = krealloc(iter_data->addr, (iter_data->count + 1) * ETH_ALEN, + GFP_ATOMIC); + if (nbuf == NULL) + return; + + memcpy(nbuf + iter_data->count * ETH_ALEN, mac, ETH_ALEN); + iter_data->addr = nbuf; + iter_data->count++; +} + +void ath9k_set_bssid_mask(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath9k_vif_iter_data iter_data; + int i, j; + u8 mask[ETH_ALEN]; + + /* + * Add primary MAC address even if it is not in active use since it + * will be configured to the hardware as the starting point and the + * BSSID mask will need to be changed if another address is active. + */ + iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC); + if (iter_data.addr) { + memcpy(iter_data.addr, sc->sc_ah->macaddr, ETH_ALEN); + iter_data.count = 1; + } else + iter_data.count = 0; + + /* Get list of all active MAC addresses */ + spin_lock_bh(&sc->wiphy_lock); + ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter, + &iter_data); + for (i = 0; i < sc->num_sec_wiphy; i++) { + if (sc->sec_wiphy[i] == NULL) + continue; + ieee80211_iterate_active_interfaces_atomic( + sc->sec_wiphy[i]->hw, ath9k_vif_iter, &iter_data); + } + spin_unlock_bh(&sc->wiphy_lock); + + /* Generate an address mask to cover all active addresses */ + memset(mask, 0, ETH_ALEN); + for (i = 0; i < iter_data.count; i++) { + u8 *a1 = iter_data.addr + i * ETH_ALEN; + for (j = i + 1; j < iter_data.count; j++) { + u8 *a2 = iter_data.addr + j * ETH_ALEN; + mask[0] |= a1[0] ^ a2[0]; + mask[1] |= a1[1] ^ a2[1]; + mask[2] |= a1[2] ^ a2[2]; + mask[3] |= a1[3] ^ a2[3]; + mask[4] |= a1[4] ^ a2[4]; + mask[5] |= a1[5] ^ a2[5]; + } + } + + kfree(iter_data.addr); + + /* Invert the mask and configure hardware */ + sc->bssidmask[0] = ~mask[0]; + sc->bssidmask[1] = ~mask[1]; + sc->bssidmask[2] = ~mask[2]; + sc->bssidmask[3] = ~mask[3]; + sc->bssidmask[4] = ~mask[4]; + sc->bssidmask[5] = ~mask[5]; + + ath9k_hw_setbssidmask(sc); +} + +int ath9k_wiphy_add(struct ath_softc *sc) +{ + int i, error; + struct ath_wiphy *aphy; + struct ieee80211_hw *hw; + u8 addr[ETH_ALEN]; + + hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy), &ath9k_ops); + if (hw == NULL) + return -ENOMEM; + + spin_lock_bh(&sc->wiphy_lock); + for (i = 0; i < sc->num_sec_wiphy; i++) { + if (sc->sec_wiphy[i] == NULL) + break; + } + + if (i == sc->num_sec_wiphy) { + /* No empty slot available; increase array length */ + struct ath_wiphy **n; + n = krealloc(sc->sec_wiphy, + (sc->num_sec_wiphy + 1) * + sizeof(struct ath_wiphy *), + GFP_ATOMIC); + if (n == NULL) { + spin_unlock_bh(&sc->wiphy_lock); + ieee80211_free_hw(hw); + return -ENOMEM; + } + n[i] = NULL; + sc->sec_wiphy = n; + sc->num_sec_wiphy++; + } + + SET_IEEE80211_DEV(hw, sc->dev); + + aphy = hw->priv; + aphy->sc = sc; + aphy->hw = hw; + sc->sec_wiphy[i] = aphy; + spin_unlock_bh(&sc->wiphy_lock); + + memcpy(addr, sc->sc_ah->macaddr, ETH_ALEN); + addr[0] |= 0x02; /* Locally managed address */ + /* + * XOR virtual wiphy index into the least significant bits to generate + * a different MAC address for each virtual wiphy. + */ + addr[5] ^= i & 0xff; + addr[4] ^= (i & 0xff00) >> 8; + addr[3] ^= (i & 0xff0000) >> 16; + + SET_IEEE80211_PERM_ADDR(hw, addr); + + ath_set_hw_capab(sc, hw); + + error = ieee80211_register_hw(hw); + + if (error == 0) { + /* Make sure wiphy scheduler is started (if enabled) */ + ath9k_wiphy_set_scheduler(sc, sc->wiphy_scheduler_int); + } + + return error; +} + +int ath9k_wiphy_del(struct ath_wiphy *aphy) +{ + struct ath_softc *sc = aphy->sc; + int i; + + spin_lock_bh(&sc->wiphy_lock); + for (i = 0; i < sc->num_sec_wiphy; i++) { + if (aphy == sc->sec_wiphy[i]) { + sc->sec_wiphy[i] = NULL; + spin_unlock_bh(&sc->wiphy_lock); + ieee80211_unregister_hw(aphy->hw); + ieee80211_free_hw(aphy->hw); + return 0; + } + } + spin_unlock_bh(&sc->wiphy_lock); + return -ENOENT; +} + +static int ath9k_send_nullfunc(struct ath_wiphy *aphy, + struct ieee80211_vif *vif, const u8 *bssid, + int ps) +{ + struct ath_softc *sc = aphy->sc; + struct ath_tx_control txctl; + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + __le16 fc; + struct ieee80211_tx_info *info; + + skb = dev_alloc_skb(24); + if (skb == NULL) + return -ENOMEM; + hdr = (struct ieee80211_hdr *) skb_put(skb, 24); + memset(hdr, 0, 24); + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS); + if (ps) + fc |= cpu_to_le16(IEEE80211_FCTL_PM); + hdr->frame_control = fc; + memcpy(hdr->addr1, bssid, ETH_ALEN); + memcpy(hdr->addr2, aphy->hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr3, bssid, ETH_ALEN); + + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS; + info->control.vif = vif; + info->control.rates[0].idx = 0; + info->control.rates[0].count = 4; + info->control.rates[1].idx = -1; + + memset(&txctl, 0, sizeof(struct ath_tx_control)); + txctl.txq = &sc->tx.txq[sc->tx.hwq_map[ATH9K_WME_AC_VO]]; + txctl.frame_type = ps ? ATH9K_INT_PAUSE : ATH9K_INT_UNPAUSE; + + if (ath_tx_start(aphy->hw, skb, &txctl) != 0) + goto exit; + + return 0; +exit: + dev_kfree_skb_any(skb); + return -1; +} + +static bool __ath9k_wiphy_pausing(struct ath_softc *sc) +{ + int i; + if (sc->pri_wiphy->state == ATH_WIPHY_PAUSING) + return true; + for (i = 0; i < sc->num_sec_wiphy; i++) { + if (sc->sec_wiphy[i] && + sc->sec_wiphy[i]->state == ATH_WIPHY_PAUSING) + return true; + } + return false; +} + +static bool ath9k_wiphy_pausing(struct ath_softc *sc) +{ + bool ret; + spin_lock_bh(&sc->wiphy_lock); + ret = __ath9k_wiphy_pausing(sc); + spin_unlock_bh(&sc->wiphy_lock); + return ret; +} + +static bool __ath9k_wiphy_scanning(struct ath_softc *sc) +{ + int i; + if (sc->pri_wiphy->state == ATH_WIPHY_SCAN) + return true; + for (i = 0; i < sc->num_sec_wiphy; i++) { + if (sc->sec_wiphy[i] && + sc->sec_wiphy[i]->state == ATH_WIPHY_SCAN) + return true; + } + return false; +} + +bool ath9k_wiphy_scanning(struct ath_softc *sc) +{ + bool ret; + spin_lock_bh(&sc->wiphy_lock); + ret = __ath9k_wiphy_scanning(sc); + spin_unlock_bh(&sc->wiphy_lock); + return ret; +} + +static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy); + +/* caller must hold wiphy_lock */ +static void __ath9k_wiphy_unpause_ch(struct ath_wiphy *aphy) +{ + if (aphy == NULL) + return; + if (aphy->chan_idx != aphy->sc->chan_idx) + return; /* wiphy not on the selected channel */ + __ath9k_wiphy_unpause(aphy); +} + +static void ath9k_wiphy_unpause_channel(struct ath_softc *sc) +{ + int i; + spin_lock_bh(&sc->wiphy_lock); + __ath9k_wiphy_unpause_ch(sc->pri_wiphy); + for (i = 0; i < sc->num_sec_wiphy; i++) + __ath9k_wiphy_unpause_ch(sc->sec_wiphy[i]); + spin_unlock_bh(&sc->wiphy_lock); +} + +void ath9k_wiphy_chan_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, chan_work); + struct ath_wiphy *aphy = sc->next_wiphy; + + if (aphy == NULL) + return; + + /* + * All pending interfaces paused; ready to change + * channels. + */ + + /* Change channels */ + mutex_lock(&sc->mutex); + /* XXX: remove me eventually */ + ath9k_update_ichannel(sc, aphy->hw, + &sc->sc_ah->channels[sc->chan_idx]); + ath_update_chainmask(sc, sc->chan_is_ht); + if (ath_set_channel(sc, aphy->hw, + &sc->sc_ah->channels[sc->chan_idx]) < 0) { + printk(KERN_DEBUG "ath9k: Failed to set channel for new " + "virtual wiphy\n"); + mutex_unlock(&sc->mutex); + return; + } + mutex_unlock(&sc->mutex); + + ath9k_wiphy_unpause_channel(sc); +} + +/* + * ath9k version of ieee80211_tx_status() for TX frames that are generated + * internally in the driver. + */ +void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ath_wiphy *aphy = hw->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); + + if (tx_info_priv && tx_info_priv->frame_type == ATH9K_INT_PAUSE && + aphy->state == ATH_WIPHY_PAUSING) { + if (!(info->flags & IEEE80211_TX_STAT_ACK)) { + printk(KERN_DEBUG "ath9k: %s: no ACK for pause " + "frame\n", wiphy_name(hw->wiphy)); + /* + * The AP did not reply; ignore this to allow us to + * continue. + */ + } + aphy->state = ATH_WIPHY_PAUSED; + if (!ath9k_wiphy_pausing(aphy->sc)) { + /* + * Drop from tasklet to work to allow mutex for channel + * change. + */ + queue_work(aphy->sc->hw->workqueue, + &aphy->sc->chan_work); + } + } + + kfree(tx_info_priv); + tx_info->rate_driver_data[0] = NULL; + + dev_kfree_skb(skb); +} + +static void ath9k_mark_paused(struct ath_wiphy *aphy) +{ + struct ath_softc *sc = aphy->sc; + aphy->state = ATH_WIPHY_PAUSED; + if (!__ath9k_wiphy_pausing(sc)) + queue_work(sc->hw->workqueue, &sc->chan_work); +} + +static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath_wiphy *aphy = data; + struct ath_vif *avp = (void *) vif->drv_priv; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + if (!vif->bss_conf.assoc) { + ath9k_mark_paused(aphy); + break; + } + /* TODO: could avoid this if already in PS mode */ + if (ath9k_send_nullfunc(aphy, vif, avp->bssid, 1)) { + printk(KERN_DEBUG "%s: failed to send PS nullfunc\n", + __func__); + ath9k_mark_paused(aphy); + } + break; + case NL80211_IFTYPE_AP: + /* Beacon transmission is paused by aphy->state change */ + ath9k_mark_paused(aphy); + break; + default: + break; + } +} + +/* caller must hold wiphy_lock */ +static int __ath9k_wiphy_pause(struct ath_wiphy *aphy) +{ + ieee80211_stop_queues(aphy->hw); + aphy->state = ATH_WIPHY_PAUSING; + /* + * TODO: handle PAUSING->PAUSED for the case where there are multiple + * active vifs (now we do it on the first vif getting ready; should be + * on the last) + */ + ieee80211_iterate_active_interfaces_atomic(aphy->hw, ath9k_pause_iter, + aphy); + return 0; +} + +int ath9k_wiphy_pause(struct ath_wiphy *aphy) +{ + int ret; + spin_lock_bh(&aphy->sc->wiphy_lock); + ret = __ath9k_wiphy_pause(aphy); + spin_unlock_bh(&aphy->sc->wiphy_lock); + return ret; +} + +static void ath9k_unpause_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath_wiphy *aphy = data; + struct ath_vif *avp = (void *) vif->drv_priv; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + if (!vif->bss_conf.assoc) + break; + ath9k_send_nullfunc(aphy, vif, avp->bssid, 0); + break; + case NL80211_IFTYPE_AP: + /* Beacon transmission is re-enabled by aphy->state change */ + break; + default: + break; + } +} + +/* caller must hold wiphy_lock */ +static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy) +{ + ieee80211_iterate_active_interfaces_atomic(aphy->hw, + ath9k_unpause_iter, aphy); + aphy->state = ATH_WIPHY_ACTIVE; + ieee80211_wake_queues(aphy->hw); + return 0; +} + +int ath9k_wiphy_unpause(struct ath_wiphy *aphy) +{ + int ret; + spin_lock_bh(&aphy->sc->wiphy_lock); + ret = __ath9k_wiphy_unpause(aphy); + spin_unlock_bh(&aphy->sc->wiphy_lock); + return ret; +} + +static void __ath9k_wiphy_mark_all_paused(struct ath_softc *sc) +{ + int i; + if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) + sc->pri_wiphy->state = ATH_WIPHY_PAUSED; + for (i = 0; i < sc->num_sec_wiphy; i++) { + if (sc->sec_wiphy[i] && + sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE) + sc->sec_wiphy[i]->state = ATH_WIPHY_PAUSED; + } +} + +/* caller must hold wiphy_lock */ +static void __ath9k_wiphy_pause_all(struct ath_softc *sc) +{ + int i; + if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE) + __ath9k_wiphy_pause(sc->pri_wiphy); + for (i = 0; i < sc->num_sec_wiphy; i++) { + if (sc->sec_wiphy[i] && + sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE) + __ath9k_wiphy_pause(sc->sec_wiphy[i]); + } +} + +int ath9k_wiphy_select(struct ath_wiphy *aphy) +{ + struct ath_softc *sc = aphy->sc; + bool now; + + spin_lock_bh(&sc->wiphy_lock); + if (__ath9k_wiphy_scanning(sc)) { + /* + * For now, we are using mac80211 sw scan and it expects to + * have full control over channel changes, so avoid wiphy + * scheduling during a scan. This could be optimized if the + * scanning control were moved into the driver. + */ + spin_unlock_bh(&sc->wiphy_lock); + return -EBUSY; + } + if (__ath9k_wiphy_pausing(sc)) { + if (sc->wiphy_select_failures == 0) + sc->wiphy_select_first_fail = jiffies; + sc->wiphy_select_failures++; + if (time_after(jiffies, sc->wiphy_select_first_fail + HZ / 2)) + { + printk(KERN_DEBUG "ath9k: Previous wiphy select timed " + "out; disable/enable hw to recover\n"); + __ath9k_wiphy_mark_all_paused(sc); + /* + * TODO: this workaround to fix hardware is unlikely to + * be specific to virtual wiphy changes. It can happen + * on normal channel change, too, and as such, this + * should really be made more generic. For example, + * tricker radio disable/enable on GTT interrupt burst + * (say, 10 GTT interrupts received without any TX + * frame being completed) + */ + spin_unlock_bh(&sc->wiphy_lock); + ath_radio_disable(sc); + ath_radio_enable(sc); + queue_work(aphy->sc->hw->workqueue, + &aphy->sc->chan_work); + return -EBUSY; /* previous select still in progress */ + } + spin_unlock_bh(&sc->wiphy_lock); + return -EBUSY; /* previous select still in progress */ + } + sc->wiphy_select_failures = 0; + + /* Store the new channel */ + sc->chan_idx = aphy->chan_idx; + sc->chan_is_ht = aphy->chan_is_ht; + sc->next_wiphy = aphy; + + __ath9k_wiphy_pause_all(sc); + now = !__ath9k_wiphy_pausing(aphy->sc); + spin_unlock_bh(&sc->wiphy_lock); + + if (now) { + /* Ready to request channel change immediately */ + queue_work(aphy->sc->hw->workqueue, &aphy->sc->chan_work); + } + + /* + * wiphys will be unpaused in ath9k_tx_status() once channel has been + * changed if any wiphy needs time to become paused. + */ + + return 0; +} + +bool ath9k_wiphy_started(struct ath_softc *sc) +{ + int i; + spin_lock_bh(&sc->wiphy_lock); + if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) { + spin_unlock_bh(&sc->wiphy_lock); + return true; + } + for (i = 0; i < sc->num_sec_wiphy; i++) { + if (sc->sec_wiphy[i] && + sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE) { + spin_unlock_bh(&sc->wiphy_lock); + return true; + } + } + spin_unlock_bh(&sc->wiphy_lock); + return false; +} + +static void ath9k_wiphy_pause_chan(struct ath_wiphy *aphy, + struct ath_wiphy *selected) +{ + if (selected->state == ATH_WIPHY_SCAN) { + if (aphy == selected) + return; + /* + * Pause all other wiphys for the duration of the scan even if + * they are on the current channel now. + */ + } else if (aphy->chan_idx == selected->chan_idx) + return; + aphy->state = ATH_WIPHY_PAUSED; + ieee80211_stop_queues(aphy->hw); +} + +void ath9k_wiphy_pause_all_forced(struct ath_softc *sc, + struct ath_wiphy *selected) +{ + int i; + spin_lock_bh(&sc->wiphy_lock); + if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE) + ath9k_wiphy_pause_chan(sc->pri_wiphy, selected); + for (i = 0; i < sc->num_sec_wiphy; i++) { + if (sc->sec_wiphy[i] && + sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE) + ath9k_wiphy_pause_chan(sc->sec_wiphy[i], selected); + } + spin_unlock_bh(&sc->wiphy_lock); +} + +void ath9k_wiphy_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + wiphy_work.work); + struct ath_wiphy *aphy = NULL; + bool first = true; + + spin_lock_bh(&sc->wiphy_lock); + + if (sc->wiphy_scheduler_int == 0) { + /* wiphy scheduler is disabled */ + spin_unlock_bh(&sc->wiphy_lock); + return; + } + +try_again: + sc->wiphy_scheduler_index++; + while (sc->wiphy_scheduler_index <= sc->num_sec_wiphy) { + aphy = sc->sec_wiphy[sc->wiphy_scheduler_index - 1]; + if (aphy && aphy->state != ATH_WIPHY_INACTIVE) + break; + + sc->wiphy_scheduler_index++; + aphy = NULL; + } + if (aphy == NULL) { + sc->wiphy_scheduler_index = 0; + if (sc->pri_wiphy->state == ATH_WIPHY_INACTIVE) { + if (first) { + first = false; + goto try_again; + } + /* No wiphy is ready to be scheduled */ + } else + aphy = sc->pri_wiphy; + } + + spin_unlock_bh(&sc->wiphy_lock); + + if (aphy && + aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN && + ath9k_wiphy_select(aphy)) { + printk(KERN_DEBUG "ath9k: Failed to schedule virtual wiphy " + "change\n"); + } + + queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work, + sc->wiphy_scheduler_int); +} + +void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int) +{ + cancel_delayed_work_sync(&sc->wiphy_work); + sc->wiphy_scheduler_int = msecs_to_jiffies(msec_int); + if (sc->wiphy_scheduler_int) + queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work, + sc->wiphy_scheduler_int); +} diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c new file mode 100644 index 00000000000..b61a071788a --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -0,0 +1,2183 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +#define BITS_PER_BYTE 8 +#define OFDM_PLCP_BITS 22 +#define HT_RC_2_MCS(_rc) ((_rc) & 0x0f) +#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) +#define L_STF 8 +#define L_LTF 8 +#define L_SIG 4 +#define HT_SIG 8 +#define HT_STF 4 +#define HT_LTF(_ns) (4 * (_ns)) +#define SYMBOL_TIME(_ns) ((_ns) << 2) /* ns * 4 us */ +#define SYMBOL_TIME_HALFGI(_ns) (((_ns) * 18 + 4) / 5) /* ns * 3.6 us */ +#define NUM_SYMBOLS_PER_USEC(_usec) (_usec >> 2) +#define NUM_SYMBOLS_PER_USEC_HALFGI(_usec) (((_usec*5)-4)/18) + +#define OFDM_SIFS_TIME 16 + +static u32 bits_per_symbol[][2] = { + /* 20MHz 40MHz */ + { 26, 54 }, /* 0: BPSK */ + { 52, 108 }, /* 1: QPSK 1/2 */ + { 78, 162 }, /* 2: QPSK 3/4 */ + { 104, 216 }, /* 3: 16-QAM 1/2 */ + { 156, 324 }, /* 4: 16-QAM 3/4 */ + { 208, 432 }, /* 5: 64-QAM 2/3 */ + { 234, 486 }, /* 6: 64-QAM 3/4 */ + { 260, 540 }, /* 7: 64-QAM 5/6 */ + { 52, 108 }, /* 8: BPSK */ + { 104, 216 }, /* 9: QPSK 1/2 */ + { 156, 324 }, /* 10: QPSK 3/4 */ + { 208, 432 }, /* 11: 16-QAM 1/2 */ + { 312, 648 }, /* 12: 16-QAM 3/4 */ + { 416, 864 }, /* 13: 64-QAM 2/3 */ + { 468, 972 }, /* 14: 64-QAM 3/4 */ + { 520, 1080 }, /* 15: 64-QAM 5/6 */ +}; + +#define IS_HT_RATE(_rate) ((_rate) & 0x80) + +static void ath_tx_send_ht_normal(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid, + struct list_head *bf_head); +static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, + struct list_head *bf_q, + int txok, int sendbar); +static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, + struct list_head *head); +static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf); +static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf, + int txok); +static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, + int nbad, int txok, bool update_rc); + +/*********************/ +/* Aggregation logic */ +/*********************/ + +static int ath_aggr_query(struct ath_softc *sc, struct ath_node *an, u8 tidno) +{ + struct ath_atx_tid *tid; + tid = ATH_AN_2_TID(an, tidno); + + if (tid->state & AGGR_ADDBA_COMPLETE || + tid->state & AGGR_ADDBA_PROGRESS) + return 1; + else + return 0; +} + +static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) +{ + struct ath_atx_ac *ac = tid->ac; + + if (tid->paused) + return; + + if (tid->sched) + return; + + tid->sched = true; + list_add_tail(&tid->list, &ac->tid_q); + + if (ac->sched) + return; + + ac->sched = true; + list_add_tail(&ac->list, &txq->axq_acq); +} + +static void ath_tx_pause_tid(struct ath_softc *sc, struct ath_atx_tid *tid) +{ + struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; + + spin_lock_bh(&txq->axq_lock); + tid->paused++; + spin_unlock_bh(&txq->axq_lock); +} + +static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid) +{ + struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; + + ASSERT(tid->paused > 0); + spin_lock_bh(&txq->axq_lock); + + tid->paused--; + + if (tid->paused > 0) + goto unlock; + + if (list_empty(&tid->buf_q)) + goto unlock; + + ath_tx_queue_tid(txq, tid); + ath_txq_schedule(sc, txq); +unlock: + spin_unlock_bh(&txq->axq_lock); +} + +static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) +{ + struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; + struct ath_buf *bf; + struct list_head bf_head; + INIT_LIST_HEAD(&bf_head); + + ASSERT(tid->paused > 0); + spin_lock_bh(&txq->axq_lock); + + tid->paused--; + + if (tid->paused > 0) { + spin_unlock_bh(&txq->axq_lock); + return; + } + + while (!list_empty(&tid->buf_q)) { + bf = list_first_entry(&tid->buf_q, struct ath_buf, list); + ASSERT(!bf_isretried(bf)); + list_move_tail(&bf->list, &bf_head); + ath_tx_send_ht_normal(sc, txq, tid, &bf_head); + } + + spin_unlock_bh(&txq->axq_lock); +} + +static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, + int seqno) +{ + int index, cindex; + + index = ATH_BA_INDEX(tid->seq_start, seqno); + cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + + tid->tx_buf[cindex] = NULL; + + while (tid->baw_head != tid->baw_tail && !tid->tx_buf[tid->baw_head]) { + INCR(tid->seq_start, IEEE80211_SEQ_MAX); + INCR(tid->baw_head, ATH_TID_MAX_BUFS); + } +} + +static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid, + struct ath_buf *bf) +{ + int index, cindex; + + if (bf_isretried(bf)) + return; + + index = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno); + cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + + ASSERT(tid->tx_buf[cindex] == NULL); + tid->tx_buf[cindex] = bf; + + if (index >= ((tid->baw_tail - tid->baw_head) & + (ATH_TID_MAX_BUFS - 1))) { + tid->baw_tail = cindex; + INCR(tid->baw_tail, ATH_TID_MAX_BUFS); + } +} + +/* + * TODO: For frame(s) that are in the retry state, we will reuse the + * sequence number(s) without setting the retry bit. The + * alternative is to give up on these and BAR the receiver's window + * forward. + */ +static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid) + +{ + struct ath_buf *bf; + struct list_head bf_head; + INIT_LIST_HEAD(&bf_head); + + for (;;) { + if (list_empty(&tid->buf_q)) + break; + + bf = list_first_entry(&tid->buf_q, struct ath_buf, list); + list_move_tail(&bf->list, &bf_head); + + if (bf_isretried(bf)) + ath_tx_update_baw(sc, tid, bf->bf_seqno); + + spin_unlock(&txq->axq_lock); + ath_tx_complete_buf(sc, bf, &bf_head, 0, 0); + spin_lock(&txq->axq_lock); + } + + tid->seq_next = tid->seq_start; + tid->baw_tail = tid->baw_head; +} + +static void ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf) +{ + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + + bf->bf_state.bf_type |= BUF_RETRY; + bf->bf_retries++; + + skb = bf->bf_mpdu; + hdr = (struct ieee80211_hdr *)skb->data; + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_RETRY); +} + +static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_buf *tbf; + + spin_lock_bh(&sc->tx.txbuflock); + ASSERT(!list_empty((&sc->tx.txbuf))); + tbf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list); + list_del(&tbf->list); + spin_unlock_bh(&sc->tx.txbuflock); + + ATH_TXBUF_RESET(tbf); + + tbf->bf_mpdu = bf->bf_mpdu; + tbf->bf_buf_addr = bf->bf_buf_addr; + *(tbf->bf_desc) = *(bf->bf_desc); + tbf->bf_state = bf->bf_state; + tbf->bf_dmacontext = bf->bf_dmacontext; + + return tbf; +} + +static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf, struct list_head *bf_q, + int txok) +{ + struct ath_node *an = NULL; + struct sk_buff *skb; + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + struct ath_atx_tid *tid = NULL; + struct ath_buf *bf_next, *bf_last = bf->bf_lastbf; + struct ath_desc *ds = bf_last->bf_desc; + struct list_head bf_head, bf_pending; + u16 seq_st = 0, acked_cnt = 0, txfail_cnt = 0; + u32 ba[WME_BA_BMP_SIZE >> 5]; + int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0; + bool rc_update = true; + + skb = bf->bf_mpdu; + hdr = (struct ieee80211_hdr *)skb->data; + + rcu_read_lock(); + + sta = ieee80211_find_sta(sc->hw, hdr->addr1); + if (!sta) { + rcu_read_unlock(); + return; + } + + an = (struct ath_node *)sta->drv_priv; + tid = ATH_AN_2_TID(an, bf->bf_tidno); + + isaggr = bf_isaggr(bf); + memset(ba, 0, WME_BA_BMP_SIZE >> 3); + + if (isaggr && txok) { + if (ATH_DS_TX_BA(ds)) { + seq_st = ATH_DS_BA_SEQ(ds); + memcpy(ba, ATH_DS_BA_BITMAP(ds), + WME_BA_BMP_SIZE >> 3); + } else { + /* + * AR5416 can become deaf/mute when BA + * issue happens. Chip needs to be reset. + * But AP code may have sychronization issues + * when perform internal reset in this routine. + * Only enable reset in STA mode for now. + */ + if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) + needreset = 1; + } + } + + INIT_LIST_HEAD(&bf_pending); + INIT_LIST_HEAD(&bf_head); + + nbad = ath_tx_num_badfrms(sc, bf, txok); + while (bf) { + txfail = txpending = 0; + bf_next = bf->bf_next; + + if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, bf->bf_seqno))) { + /* transmit completion, subframe is + * acked by block ack */ + acked_cnt++; + } else if (!isaggr && txok) { + /* transmit completion */ + acked_cnt++; + } else { + if (!(tid->state & AGGR_CLEANUP) && + ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) { + if (bf->bf_retries < ATH_MAX_SW_RETRIES) { + ath_tx_set_retry(sc, bf); + txpending = 1; + } else { + bf->bf_state.bf_type |= BUF_XRETRY; + txfail = 1; + sendbar = 1; + txfail_cnt++; + } + } else { + /* + * cleanup in progress, just fail + * the un-acked sub-frames + */ + txfail = 1; + } + } + + if (bf_next == NULL) { + INIT_LIST_HEAD(&bf_head); + } else { + ASSERT(!list_empty(bf_q)); + list_move_tail(&bf->list, &bf_head); + } + + if (!txpending) { + /* + * complete the acked-ones/xretried ones; update + * block-ack window + */ + spin_lock_bh(&txq->axq_lock); + ath_tx_update_baw(sc, tid, bf->bf_seqno); + spin_unlock_bh(&txq->axq_lock); + + if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { + ath_tx_rc_status(bf, ds, nbad, txok, true); + rc_update = false; + } else { + ath_tx_rc_status(bf, ds, nbad, txok, false); + } + + ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar); + } else { + /* retry the un-acked ones */ + if (bf->bf_next == NULL && bf_last->bf_stale) { + struct ath_buf *tbf; + + tbf = ath_clone_txbuf(sc, bf_last); + ath9k_hw_cleartxdesc(sc->sc_ah, tbf->bf_desc); + list_add_tail(&tbf->list, &bf_head); + } else { + /* + * Clear descriptor status words for + * software retry + */ + ath9k_hw_cleartxdesc(sc->sc_ah, bf->bf_desc); + } + + /* + * Put this buffer to the temporary pending + * queue to retain ordering + */ + list_splice_tail_init(&bf_head, &bf_pending); + } + + bf = bf_next; + } + + if (tid->state & AGGR_CLEANUP) { + if (tid->baw_head == tid->baw_tail) { + tid->state &= ~AGGR_ADDBA_COMPLETE; + tid->addba_exchangeattempts = 0; + tid->state &= ~AGGR_CLEANUP; + + /* send buffered frames as singles */ + ath_tx_flush_tid(sc, tid); + } + rcu_read_unlock(); + return; + } + + /* prepend un-acked frames to the beginning of the pending frame queue */ + if (!list_empty(&bf_pending)) { + spin_lock_bh(&txq->axq_lock); + list_splice(&bf_pending, &tid->buf_q); + ath_tx_queue_tid(txq, tid); + spin_unlock_bh(&txq->axq_lock); + } + + rcu_read_unlock(); + + if (needreset) + ath_reset(sc, false); +} + +static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, + struct ath_atx_tid *tid) +{ + const struct ath_rate_table *rate_table = sc->cur_rate_table; + struct sk_buff *skb; + struct ieee80211_tx_info *tx_info; + struct ieee80211_tx_rate *rates; + struct ath_tx_info_priv *tx_info_priv; + u32 max_4ms_framelen, frmlen; + u16 aggr_limit, legacy = 0, maxampdu; + int i; + + skb = bf->bf_mpdu; + tx_info = IEEE80211_SKB_CB(skb); + rates = tx_info->control.rates; + tx_info_priv = (struct ath_tx_info_priv *)tx_info->rate_driver_data[0]; + + /* + * Find the lowest frame length among the rate series that will have a + * 4ms transmit duration. + * TODO - TXOP limit needs to be considered. + */ + max_4ms_framelen = ATH_AMPDU_LIMIT_MAX; + + for (i = 0; i < 4; i++) { + if (rates[i].count) { + if (!WLAN_RC_PHY_HT(rate_table->info[rates[i].idx].phy)) { + legacy = 1; + break; + } + + frmlen = rate_table->info[rates[i].idx].max_4ms_framelen; + max_4ms_framelen = min(max_4ms_framelen, frmlen); + } + } + + /* + * limit aggregate size by the minimum rate if rate selected is + * not a probe rate, if rate selected is a probe rate then + * avoid aggregation of this packet. + */ + if (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE || legacy) + return 0; + + aggr_limit = min(max_4ms_framelen, (u32)ATH_AMPDU_LIMIT_DEFAULT); + + /* + * h/w can accept aggregates upto 16 bit lengths (65535). + * The IE, however can hold upto 65536, which shows up here + * as zero. Ignore 65536 since we are constrained by hw. + */ + maxampdu = tid->an->maxampdu; + if (maxampdu) + aggr_limit = min(aggr_limit, maxampdu); + + return aggr_limit; +} + +/* + * Returns the number of delimiters to be added to + * meet the minimum required mpdudensity. + * caller should make sure that the rate is HT rate . + */ +static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, + struct ath_buf *bf, u16 frmlen) +{ + const struct ath_rate_table *rt = sc->cur_rate_table; + struct sk_buff *skb = bf->bf_mpdu; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + u32 nsymbits, nsymbols, mpdudensity; + u16 minlen; + u8 rc, flags, rix; + int width, half_gi, ndelim, mindelim; + + /* Select standard number of delimiters based on frame length alone */ + ndelim = ATH_AGGR_GET_NDELIM(frmlen); + + /* + * If encryption enabled, hardware requires some more padding between + * subframes. + * TODO - this could be improved to be dependent on the rate. + * The hardware can keep up at lower rates, but not higher rates + */ + if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR) + ndelim += ATH_AGGR_ENCRYPTDELIM; + + /* + * Convert desired mpdu density from microeconds to bytes based + * on highest rate in rate series (i.e. first rate) to determine + * required minimum length for subframe. Take into account + * whether high rate is 20 or 40Mhz and half or full GI. + */ + mpdudensity = tid->an->mpdudensity; + + /* + * If there is no mpdu density restriction, no further calculation + * is needed. + */ + if (mpdudensity == 0) + return ndelim; + + rix = tx_info->control.rates[0].idx; + flags = tx_info->control.rates[0].flags; + rc = rt->info[rix].ratecode; + width = (flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ? 1 : 0; + half_gi = (flags & IEEE80211_TX_RC_SHORT_GI) ? 1 : 0; + + if (half_gi) + nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(mpdudensity); + else + nsymbols = NUM_SYMBOLS_PER_USEC(mpdudensity); + + if (nsymbols == 0) + nsymbols = 1; + + nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width]; + minlen = (nsymbols * nsymbits) / BITS_PER_BYTE; + + if (frmlen < minlen) { + mindelim = (minlen - frmlen) / ATH_AGGR_DELIM_SZ; + ndelim = max(mindelim, ndelim); + } + + return ndelim; +} + +static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, + struct ath_atx_tid *tid, + struct list_head *bf_q) +{ +#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) + struct ath_buf *bf, *bf_first, *bf_prev = NULL; + int rl = 0, nframes = 0, ndelim, prev_al = 0; + u16 aggr_limit = 0, al = 0, bpad = 0, + al_delta, h_baw = tid->baw_size / 2; + enum ATH_AGGR_STATUS status = ATH_AGGR_DONE; + + bf_first = list_first_entry(&tid->buf_q, struct ath_buf, list); + + do { + bf = list_first_entry(&tid->buf_q, struct ath_buf, list); + + /* do not step over block-ack window */ + if (!BAW_WITHIN(tid->seq_start, tid->baw_size, bf->bf_seqno)) { + status = ATH_AGGR_BAW_CLOSED; + break; + } + + if (!rl) { + aggr_limit = ath_lookup_rate(sc, bf, tid); + rl = 1; + } + + /* do not exceed aggregation limit */ + al_delta = ATH_AGGR_DELIM_SZ + bf->bf_frmlen; + + if (nframes && + (aggr_limit < (al + bpad + al_delta + prev_al))) { + status = ATH_AGGR_LIMITED; + break; + } + + /* do not exceed subframe limit */ + if (nframes >= min((int)h_baw, ATH_AMPDU_SUBFRAME_DEFAULT)) { + status = ATH_AGGR_LIMITED; + break; + } + nframes++; + + /* add padding for previous frame to aggregation length */ + al += bpad + al_delta; + + /* + * Get the delimiters needed to meet the MPDU + * density for this node. + */ + ndelim = ath_compute_num_delims(sc, tid, bf_first, bf->bf_frmlen); + bpad = PADBYTES(al_delta) + (ndelim << 2); + + bf->bf_next = NULL; + bf->bf_desc->ds_link = 0; + + /* link buffers of this frame to the aggregate */ + ath_tx_addto_baw(sc, tid, bf); + ath9k_hw_set11n_aggr_middle(sc->sc_ah, bf->bf_desc, ndelim); + list_move_tail(&bf->list, bf_q); + if (bf_prev) { + bf_prev->bf_next = bf; + bf_prev->bf_desc->ds_link = bf->bf_daddr; + } + bf_prev = bf; + } while (!list_empty(&tid->buf_q)); + + bf_first->bf_al = al; + bf_first->bf_nframes = nframes; + + return status; +#undef PADBYTES +} + +static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid) +{ + struct ath_buf *bf; + enum ATH_AGGR_STATUS status; + struct list_head bf_q; + + do { + if (list_empty(&tid->buf_q)) + return; + + INIT_LIST_HEAD(&bf_q); + + status = ath_tx_form_aggr(sc, tid, &bf_q); + + /* + * no frames picked up to be aggregated; + * block-ack window is not open. + */ + if (list_empty(&bf_q)) + break; + + bf = list_first_entry(&bf_q, struct ath_buf, list); + bf->bf_lastbf = list_entry(bf_q.prev, struct ath_buf, list); + + /* if only one frame, send as non-aggregate */ + if (bf->bf_nframes == 1) { + bf->bf_state.bf_type &= ~BUF_AGGR; + ath9k_hw_clr11n_aggr(sc->sc_ah, bf->bf_desc); + ath_buf_set_rate(sc, bf); + ath_tx_txqaddbuf(sc, txq, &bf_q); + continue; + } + + /* setup first desc of aggregate */ + bf->bf_state.bf_type |= BUF_AGGR; + ath_buf_set_rate(sc, bf); + ath9k_hw_set11n_aggr_first(sc->sc_ah, bf->bf_desc, bf->bf_al); + + /* anchor last desc of aggregate */ + ath9k_hw_set11n_aggr_last(sc->sc_ah, bf->bf_lastbf->bf_desc); + + txq->axq_aggr_depth++; + ath_tx_txqaddbuf(sc, txq, &bf_q); + + } while (txq->axq_depth < ATH_AGGR_MIN_QDEPTH && + status != ATH_AGGR_BAW_CLOSED); +} + +int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, + u16 tid, u16 *ssn) +{ + struct ath_atx_tid *txtid; + struct ath_node *an; + + an = (struct ath_node *)sta->drv_priv; + + if (sc->sc_flags & SC_OP_TXAGGR) { + txtid = ATH_AN_2_TID(an, tid); + txtid->state |= AGGR_ADDBA_PROGRESS; + ath_tx_pause_tid(sc, txtid); + *ssn = txtid->seq_start; + } + + return 0; +} + +int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) +{ + struct ath_node *an = (struct ath_node *)sta->drv_priv; + struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); + struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum]; + struct ath_buf *bf; + struct list_head bf_head; + INIT_LIST_HEAD(&bf_head); + + if (txtid->state & AGGR_CLEANUP) + return 0; + + if (!(txtid->state & AGGR_ADDBA_COMPLETE)) { + txtid->state &= ~AGGR_ADDBA_PROGRESS; + txtid->addba_exchangeattempts = 0; + return 0; + } + + ath_tx_pause_tid(sc, txtid); + + /* drop all software retried frames and mark this TID */ + spin_lock_bh(&txq->axq_lock); + while (!list_empty(&txtid->buf_q)) { + bf = list_first_entry(&txtid->buf_q, struct ath_buf, list); + if (!bf_isretried(bf)) { + /* + * NB: it's based on the assumption that + * software retried frame will always stay + * at the head of software queue. + */ + break; + } + list_move_tail(&bf->list, &bf_head); + ath_tx_update_baw(sc, txtid, bf->bf_seqno); + ath_tx_complete_buf(sc, bf, &bf_head, 0, 0); + } + spin_unlock_bh(&txq->axq_lock); + + if (txtid->baw_head != txtid->baw_tail) { + txtid->state |= AGGR_CLEANUP; + } else { + txtid->state &= ~AGGR_ADDBA_COMPLETE; + txtid->addba_exchangeattempts = 0; + ath_tx_flush_tid(sc, txtid); + } + + return 0; +} + +void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) +{ + struct ath_atx_tid *txtid; + struct ath_node *an; + + an = (struct ath_node *)sta->drv_priv; + + if (sc->sc_flags & SC_OP_TXAGGR) { + txtid = ATH_AN_2_TID(an, tid); + txtid->baw_size = + IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor; + txtid->state |= AGGR_ADDBA_COMPLETE; + txtid->state &= ~AGGR_ADDBA_PROGRESS; + ath_tx_resume_tid(sc, txtid); + } +} + +bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno) +{ + struct ath_atx_tid *txtid; + + if (!(sc->sc_flags & SC_OP_TXAGGR)) + return false; + + txtid = ATH_AN_2_TID(an, tidno); + + if (!(txtid->state & AGGR_ADDBA_COMPLETE)) { + if (!(txtid->state & AGGR_ADDBA_PROGRESS) && + (txtid->addba_exchangeattempts < ADDBA_EXCHANGE_ATTEMPTS)) { + txtid->addba_exchangeattempts++; + return true; + } + } + + return false; +} + +/********************/ +/* Queue Management */ +/********************/ + +static void ath_txq_drain_pending_buffers(struct ath_softc *sc, + struct ath_txq *txq) +{ + struct ath_atx_ac *ac, *ac_tmp; + struct ath_atx_tid *tid, *tid_tmp; + + list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) { + list_del(&ac->list); + ac->sched = false; + list_for_each_entry_safe(tid, tid_tmp, &ac->tid_q, list) { + list_del(&tid->list); + tid->sched = false; + ath_tid_drain(sc, txq, tid); + } + } +} + +struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath9k_tx_queue_info qi; + int qnum; + + memset(&qi, 0, sizeof(qi)); + qi.tqi_subtype = subtype; + qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; + qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; + qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; + qi.tqi_physCompBuf = 0; + + /* + * Enable interrupts only for EOL and DESC conditions. + * We mark tx descriptors to receive a DESC interrupt + * when a tx queue gets deep; otherwise waiting for the + * EOL to reap descriptors. Note that this is done to + * reduce interrupt load and this only defers reaping + * descriptors, never transmitting frames. Aside from + * reducing interrupts this also permits more concurrency. + * The only potential downside is if the tx queue backs + * up in which case the top half of the kernel may backup + * due to a lack of tx descriptors. + * + * The UAPSD queue is an exception, since we take a desc- + * based intr on the EOSP frames. + */ + if (qtype == ATH9K_TX_QUEUE_UAPSD) + qi.tqi_qflags = TXQ_FLAG_TXDESCINT_ENABLE; + else + qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | + TXQ_FLAG_TXDESCINT_ENABLE; + qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi); + if (qnum == -1) { + /* + * NB: don't print a message, this happens + * normally on parts with too few tx queues + */ + return NULL; + } + if (qnum >= ARRAY_SIZE(sc->tx.txq)) { + DPRINTF(sc, ATH_DBG_FATAL, + "qnum %u out of range, max %u!\n", + qnum, (unsigned int)ARRAY_SIZE(sc->tx.txq)); + ath9k_hw_releasetxqueue(ah, qnum); + return NULL; + } + if (!ATH_TXQ_SETUP(sc, qnum)) { + struct ath_txq *txq = &sc->tx.txq[qnum]; + + txq->axq_qnum = qnum; + txq->axq_link = NULL; + INIT_LIST_HEAD(&txq->axq_q); + INIT_LIST_HEAD(&txq->axq_acq); + spin_lock_init(&txq->axq_lock); + txq->axq_depth = 0; + txq->axq_aggr_depth = 0; + txq->axq_totalqueued = 0; + txq->axq_linkbuf = NULL; + sc->tx.txqsetup |= 1<<qnum; + } + return &sc->tx.txq[qnum]; +} + +static int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype) +{ + int qnum; + + switch (qtype) { + case ATH9K_TX_QUEUE_DATA: + if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) { + DPRINTF(sc, ATH_DBG_FATAL, + "HAL AC %u out of range, max %zu!\n", + haltype, ARRAY_SIZE(sc->tx.hwq_map)); + return -1; + } + qnum = sc->tx.hwq_map[haltype]; + break; + case ATH9K_TX_QUEUE_BEACON: + qnum = sc->beacon.beaconq; + break; + case ATH9K_TX_QUEUE_CAB: + qnum = sc->beacon.cabq->axq_qnum; + break; + default: + qnum = -1; + } + return qnum; +} + +struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb) +{ + struct ath_txq *txq = NULL; + int qnum; + + qnum = ath_get_hal_qnum(skb_get_queue_mapping(skb), sc); + txq = &sc->tx.txq[qnum]; + + spin_lock_bh(&txq->axq_lock); + + if (txq->axq_depth >= (ATH_TXBUF - 20)) { + DPRINTF(sc, ATH_DBG_XMIT, + "TX queue: %d is full, depth: %d\n", + qnum, txq->axq_depth); + ieee80211_stop_queue(sc->hw, skb_get_queue_mapping(skb)); + txq->stopped = 1; + spin_unlock_bh(&txq->axq_lock); + return NULL; + } + + spin_unlock_bh(&txq->axq_lock); + + return txq; +} + +int ath_txq_update(struct ath_softc *sc, int qnum, + struct ath9k_tx_queue_info *qinfo) +{ + struct ath_hw *ah = sc->sc_ah; + int error = 0; + struct ath9k_tx_queue_info qi; + + if (qnum == sc->beacon.beaconq) { + /* + * XXX: for beacon queue, we just save the parameter. + * It will be picked up by ath_beaconq_config when + * it's necessary. + */ + sc->beacon.beacon_qi = *qinfo; + return 0; + } + + ASSERT(sc->tx.txq[qnum].axq_qnum == qnum); + + ath9k_hw_get_txq_props(ah, qnum, &qi); + qi.tqi_aifs = qinfo->tqi_aifs; + qi.tqi_cwmin = qinfo->tqi_cwmin; + qi.tqi_cwmax = qinfo->tqi_cwmax; + qi.tqi_burstTime = qinfo->tqi_burstTime; + qi.tqi_readyTime = qinfo->tqi_readyTime; + + if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to update hardware queue %u!\n", qnum); + error = -EIO; + } else { + ath9k_hw_resettxqueue(ah, qnum); + } + + return error; +} + +int ath_cabq_update(struct ath_softc *sc) +{ + struct ath9k_tx_queue_info qi; + int qnum = sc->beacon.cabq->axq_qnum; + + ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi); + /* + * Ensure the readytime % is within the bounds. + */ + if (sc->config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND) + sc->config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND; + else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND) + sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND; + + qi.tqi_readyTime = (sc->beacon_interval * + sc->config.cabqReadytime) / 100; + ath_txq_update(sc, qnum, &qi); + + return 0; +} + +/* + * Drain a given TX queue (could be Beacon or Data) + * + * This assumes output has been stopped and + * we do not need to block ath_tx_tasklet. + */ +void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) +{ + struct ath_buf *bf, *lastbf; + struct list_head bf_head; + + INIT_LIST_HEAD(&bf_head); + + for (;;) { + spin_lock_bh(&txq->axq_lock); + + if (list_empty(&txq->axq_q)) { + txq->axq_link = NULL; + txq->axq_linkbuf = NULL; + spin_unlock_bh(&txq->axq_lock); + break; + } + + bf = list_first_entry(&txq->axq_q, struct ath_buf, list); + + if (bf->bf_stale) { + list_del(&bf->list); + spin_unlock_bh(&txq->axq_lock); + + spin_lock_bh(&sc->tx.txbuflock); + list_add_tail(&bf->list, &sc->tx.txbuf); + spin_unlock_bh(&sc->tx.txbuflock); + continue; + } + + lastbf = bf->bf_lastbf; + if (!retry_tx) + lastbf->bf_desc->ds_txstat.ts_flags = + ATH9K_TX_SW_ABORTED; + + /* remove ath_buf's of the same mpdu from txq */ + list_cut_position(&bf_head, &txq->axq_q, &lastbf->list); + txq->axq_depth--; + + spin_unlock_bh(&txq->axq_lock); + + if (bf_isampdu(bf)) + ath_tx_complete_aggr(sc, txq, bf, &bf_head, 0); + else + ath_tx_complete_buf(sc, bf, &bf_head, 0, 0); + } + + /* flush any pending frames if aggregation is enabled */ + if (sc->sc_flags & SC_OP_TXAGGR) { + if (!retry_tx) { + spin_lock_bh(&txq->axq_lock); + ath_txq_drain_pending_buffers(sc, txq); + spin_unlock_bh(&txq->axq_lock); + } + } +} + +void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_txq *txq; + int i, npend = 0; + + if (sc->sc_flags & SC_OP_INVALID) + return; + + /* Stop beacon queue */ + ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); + + /* Stop data queues */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + txq = &sc->tx.txq[i]; + ath9k_hw_stoptxdma(ah, txq->axq_qnum); + npend += ath9k_hw_numtxpending(ah, txq->axq_qnum); + } + } + + if (npend) { + int r; + + DPRINTF(sc, ATH_DBG_XMIT, "Unable to stop TxDMA. Reset HAL!\n"); + + spin_lock_bh(&sc->sc_resetlock); + r = ath9k_hw_reset(ah, sc->sc_ah->curchan, true); + if (r) + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to reset hardware; reset status %d\n", + r); + spin_unlock_bh(&sc->sc_resetlock); + } + + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) + ath_draintxq(sc, &sc->tx.txq[i], retry_tx); + } +} + +void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) +{ + ath9k_hw_releasetxqueue(sc->sc_ah, txq->axq_qnum); + sc->tx.txqsetup &= ~(1<<txq->axq_qnum); +} + +void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_atx_ac *ac; + struct ath_atx_tid *tid; + + if (list_empty(&txq->axq_acq)) + return; + + ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list); + list_del(&ac->list); + ac->sched = false; + + do { + if (list_empty(&ac->tid_q)) + return; + + tid = list_first_entry(&ac->tid_q, struct ath_atx_tid, list); + list_del(&tid->list); + tid->sched = false; + + if (tid->paused) + continue; + + if ((txq->axq_depth % 2) == 0) + ath_tx_sched_aggr(sc, txq, tid); + + /* + * add tid to round-robin queue if more frames + * are pending for the tid + */ + if (!list_empty(&tid->buf_q)) + ath_tx_queue_tid(txq, tid); + + break; + } while (!list_empty(&ac->tid_q)); + + if (!list_empty(&ac->tid_q)) { + if (!ac->sched) { + ac->sched = true; + list_add_tail(&ac->list, &txq->axq_acq); + } + } +} + +int ath_tx_setup(struct ath_softc *sc, int haltype) +{ + struct ath_txq *txq; + + if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) { + DPRINTF(sc, ATH_DBG_FATAL, + "HAL AC %u out of range, max %zu!\n", + haltype, ARRAY_SIZE(sc->tx.hwq_map)); + return 0; + } + txq = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, haltype); + if (txq != NULL) { + sc->tx.hwq_map[haltype] = txq->axq_qnum; + return 1; + } else + return 0; +} + +/***********/ +/* TX, DMA */ +/***********/ + +/* + * Insert a chain of ath_buf (descriptors) on a txq and + * assume the descriptors are already chained together by caller. + */ +static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, + struct list_head *head) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_buf *bf; + + /* + * Insert the frame on the outbound list and + * pass it on to the hardware. + */ + + if (list_empty(head)) + return; + + bf = list_first_entry(head, struct ath_buf, list); + + list_splice_tail_init(head, &txq->axq_q); + txq->axq_depth++; + txq->axq_totalqueued++; + txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list); + + DPRINTF(sc, ATH_DBG_QUEUE, + "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth); + + if (txq->axq_link == NULL) { + ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); + DPRINTF(sc, ATH_DBG_XMIT, + "TXDP[%u] = %llx (%p)\n", + txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc); + } else { + *txq->axq_link = bf->bf_daddr; + DPRINTF(sc, ATH_DBG_XMIT, "link[%u] (%p)=%llx (%p)\n", + txq->axq_qnum, txq->axq_link, + ito64(bf->bf_daddr), bf->bf_desc); + } + txq->axq_link = &(bf->bf_lastbf->bf_desc->ds_link); + ath9k_hw_txstart(ah, txq->axq_qnum); +} + +static struct ath_buf *ath_tx_get_buffer(struct ath_softc *sc) +{ + struct ath_buf *bf = NULL; + + spin_lock_bh(&sc->tx.txbuflock); + + if (unlikely(list_empty(&sc->tx.txbuf))) { + spin_unlock_bh(&sc->tx.txbuflock); + return NULL; + } + + bf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list); + list_del(&bf->list); + + spin_unlock_bh(&sc->tx.txbuflock); + + return bf; +} + +static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, + struct list_head *bf_head, + struct ath_tx_control *txctl) +{ + struct ath_buf *bf; + + bf = list_first_entry(bf_head, struct ath_buf, list); + bf->bf_state.bf_type |= BUF_AMPDU; + + /* + * Do not queue to h/w when any of the following conditions is true: + * - there are pending frames in software queue + * - the TID is currently paused for ADDBA/BAR request + * - seqno is not within block-ack window + * - h/w queue depth exceeds low water mark + */ + if (!list_empty(&tid->buf_q) || tid->paused || + !BAW_WITHIN(tid->seq_start, tid->baw_size, bf->bf_seqno) || + txctl->txq->axq_depth >= ATH_AGGR_MIN_QDEPTH) { + /* + * Add this frame to software queue for scheduling later + * for aggregation. + */ + list_move_tail(&bf->list, &tid->buf_q); + ath_tx_queue_tid(txctl->txq, tid); + return; + } + + /* Add sub-frame to BAW */ + ath_tx_addto_baw(sc, tid, bf); + + /* Queue to h/w without aggregation */ + bf->bf_nframes = 1; + bf->bf_lastbf = bf; + ath_buf_set_rate(sc, bf); + ath_tx_txqaddbuf(sc, txctl->txq, bf_head); +} + +static void ath_tx_send_ht_normal(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid, + struct list_head *bf_head) +{ + struct ath_buf *bf; + + bf = list_first_entry(bf_head, struct ath_buf, list); + bf->bf_state.bf_type &= ~BUF_AMPDU; + + /* update starting sequence number for subsequent ADDBA request */ + INCR(tid->seq_start, IEEE80211_SEQ_MAX); + + bf->bf_nframes = 1; + bf->bf_lastbf = bf; + ath_buf_set_rate(sc, bf); + ath_tx_txqaddbuf(sc, txq, bf_head); +} + +static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, + struct list_head *bf_head) +{ + struct ath_buf *bf; + + bf = list_first_entry(bf_head, struct ath_buf, list); + + bf->bf_lastbf = bf; + bf->bf_nframes = 1; + ath_buf_set_rate(sc, bf); + ath_tx_txqaddbuf(sc, txq, bf_head); +} + +static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + enum ath9k_pkt_type htype; + __le16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + + if (ieee80211_is_beacon(fc)) + htype = ATH9K_PKT_TYPE_BEACON; + else if (ieee80211_is_probe_resp(fc)) + htype = ATH9K_PKT_TYPE_PROBE_RESP; + else if (ieee80211_is_atim(fc)) + htype = ATH9K_PKT_TYPE_ATIM; + else if (ieee80211_is_pspoll(fc)) + htype = ATH9K_PKT_TYPE_PSPOLL; + else + htype = ATH9K_PKT_TYPE_NORMAL; + + return htype; +} + +static bool is_pae(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + __le16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + + if (ieee80211_is_data(fc)) { + if (ieee80211_is_nullfunc(fc) || + /* Port Access Entity (IEEE 802.1X) */ + (skb->protocol == cpu_to_be16(ETH_P_PAE))) { + return true; + } + } + + return false; +} + +static int get_hw_crypto_keytype(struct sk_buff *skb) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + + if (tx_info->control.hw_key) { + if (tx_info->control.hw_key->alg == ALG_WEP) + return ATH9K_KEY_TYPE_WEP; + else if (tx_info->control.hw_key->alg == ALG_TKIP) + return ATH9K_KEY_TYPE_TKIP; + else if (tx_info->control.hw_key->alg == ALG_CCMP) + return ATH9K_KEY_TYPE_AES; + } + + return ATH9K_KEY_TYPE_CLEAR; +} + +static void assign_aggr_tid_seqno(struct sk_buff *skb, + struct ath_buf *bf) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr; + struct ath_node *an; + struct ath_atx_tid *tid; + __le16 fc; + u8 *qc; + + if (!tx_info->control.sta) + return; + + an = (struct ath_node *)tx_info->control.sta->drv_priv; + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); + bf->bf_tidno = qc[0] & 0xf; + } + + /* + * For HT capable stations, we save tidno for later use. + * We also override seqno set by upper layer with the one + * in tx aggregation state. + * + * If fragmentation is on, the sequence number is + * not overridden, since it has been + * incremented by the fragmentation routine. + * + * FIXME: check if the fragmentation threshold exceeds + * IEEE80211 max. + */ + tid = ATH_AN_2_TID(an, bf->bf_tidno); + hdr->seq_ctrl = cpu_to_le16(tid->seq_next << + IEEE80211_SEQ_SEQ_SHIFT); + bf->bf_seqno = tid->seq_next; + INCR(tid->seq_next, IEEE80211_SEQ_MAX); +} + +static int setup_tx_flags(struct ath_softc *sc, struct sk_buff *skb, + struct ath_txq *txq) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + int flags = 0; + + flags |= ATH9K_TXDESC_CLRDMASK; /* needed for crypto errors */ + flags |= ATH9K_TXDESC_INTREQ; + + if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) + flags |= ATH9K_TXDESC_NOACK; + + return flags; +} + +/* + * rix - rate index + * pktlen - total bytes (delims + data + fcs + pads + pad delims) + * width - 0 for 20 MHz, 1 for 40 MHz + * half_gi - to use 4us v/s 3.6 us for symbol time + */ +static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf, + int width, int half_gi, bool shortPreamble) +{ + const struct ath_rate_table *rate_table = sc->cur_rate_table; + u32 nbits, nsymbits, duration, nsymbols; + u8 rc; + int streams, pktlen; + + pktlen = bf_isaggr(bf) ? bf->bf_al : bf->bf_frmlen; + rc = rate_table->info[rix].ratecode; + + /* for legacy rates, use old function to compute packet duration */ + if (!IS_HT_RATE(rc)) + return ath9k_hw_computetxtime(sc->sc_ah, rate_table, pktlen, + rix, shortPreamble); + + /* find number of symbols: PLCP + data */ + nbits = (pktlen << 3) + OFDM_PLCP_BITS; + nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width]; + nsymbols = (nbits + nsymbits - 1) / nsymbits; + + if (!half_gi) + duration = SYMBOL_TIME(nsymbols); + else + duration = SYMBOL_TIME_HALFGI(nsymbols); + + /* addup duration for legacy/ht training and signal fields */ + streams = HT_RC_2_STREAMS(rc); + duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams); + + return duration; +} + +static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) +{ + const struct ath_rate_table *rt = sc->cur_rate_table; + struct ath9k_11n_rate_series series[4]; + struct sk_buff *skb; + struct ieee80211_tx_info *tx_info; + struct ieee80211_tx_rate *rates; + struct ieee80211_hdr *hdr; + int i, flags = 0; + u8 rix = 0, ctsrate = 0; + bool is_pspoll; + + memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4); + + skb = bf->bf_mpdu; + tx_info = IEEE80211_SKB_CB(skb); + rates = tx_info->control.rates; + hdr = (struct ieee80211_hdr *)skb->data; + is_pspoll = ieee80211_is_pspoll(hdr->frame_control); + + /* + * We check if Short Preamble is needed for the CTS rate by + * checking the BSS's global flag. + * But for the rate series, IEEE80211_TX_RC_USE_SHORT_PREAMBLE is used. + */ + if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) + ctsrate = rt->info[tx_info->control.rts_cts_rate_idx].ratecode | + rt->info[tx_info->control.rts_cts_rate_idx].short_preamble; + else + ctsrate = rt->info[tx_info->control.rts_cts_rate_idx].ratecode; + + /* + * ATH9K_TXDESC_RTSENA and ATH9K_TXDESC_CTSENA are mutually exclusive. + * Check the first rate in the series to decide whether RTS/CTS + * or CTS-to-self has to be used. + */ + if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + flags = ATH9K_TXDESC_CTSENA; + else if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) + flags = ATH9K_TXDESC_RTSENA; + + /* FIXME: Handle aggregation protection */ + if (sc->config.ath_aggr_prot && + (!bf_isaggr(bf) || (bf_isaggr(bf) && bf->bf_al < 8192))) { + flags = ATH9K_TXDESC_RTSENA; + } + + /* For AR5416 - RTS cannot be followed by a frame larger than 8K */ + if (bf_isaggr(bf) && (bf->bf_al > sc->sc_ah->caps.rts_aggr_limit)) + flags &= ~(ATH9K_TXDESC_RTSENA); + + for (i = 0; i < 4; i++) { + if (!rates[i].count || (rates[i].idx < 0)) + continue; + + rix = rates[i].idx; + series[i].Tries = rates[i].count; + series[i].ChSel = sc->tx_chainmask; + + if (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + series[i].Rate = rt->info[rix].ratecode | + rt->info[rix].short_preamble; + else + series[i].Rate = rt->info[rix].ratecode; + + if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) + series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS; + if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + series[i].RateFlags |= ATH9K_RATESERIES_2040; + if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI) + series[i].RateFlags |= ATH9K_RATESERIES_HALFGI; + + series[i].PktDuration = ath_pkt_duration(sc, rix, bf, + (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) != 0, + (rates[i].flags & IEEE80211_TX_RC_SHORT_GI), + (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)); + } + + /* set dur_update_en for l-sig computation except for PS-Poll frames */ + ath9k_hw_set11n_ratescenario(sc->sc_ah, bf->bf_desc, + bf->bf_lastbf->bf_desc, + !is_pspoll, ctsrate, + 0, series, 4, flags); + + if (sc->config.ath_aggr_prot && flags) + ath9k_hw_set11n_burstduration(sc->sc_ah, bf->bf_desc, 8192); +} + +static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, + struct sk_buff *skb, + struct ath_tx_control *txctl) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ath_tx_info_priv *tx_info_priv; + int hdrlen; + __le16 fc; + + tx_info_priv = kzalloc(sizeof(*tx_info_priv), GFP_ATOMIC); + if (unlikely(!tx_info_priv)) + return -ENOMEM; + tx_info->rate_driver_data[0] = tx_info_priv; + tx_info_priv->aphy = aphy; + tx_info_priv->frame_type = txctl->frame_type; + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + fc = hdr->frame_control; + + ATH_TXBUF_RESET(bf); + + bf->bf_frmlen = skb->len + FCS_LEN - (hdrlen & 3); + + if (conf_is_ht(&sc->hw->conf) && !is_pae(skb)) + bf->bf_state.bf_type |= BUF_HT; + + bf->bf_flags = setup_tx_flags(sc, skb, txctl->txq); + + bf->bf_keytype = get_hw_crypto_keytype(skb); + if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR) { + bf->bf_frmlen += tx_info->control.hw_key->icv_len; + bf->bf_keyix = tx_info->control.hw_key->hw_key_idx; + } else { + bf->bf_keyix = ATH9K_TXKEYIX_INVALID; + } + + if (ieee80211_is_data_qos(fc) && (sc->sc_flags & SC_OP_TXAGGR)) + assign_aggr_tid_seqno(skb, bf); + + bf->bf_mpdu = skb; + + bf->bf_dmacontext = dma_map_single(sc->dev, skb->data, + skb->len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(sc->dev, bf->bf_dmacontext))) { + bf->bf_mpdu = NULL; + kfree(tx_info_priv); + tx_info->rate_driver_data[0] = NULL; + DPRINTF(sc, ATH_DBG_FATAL, "dma_mapping_error() on TX\n"); + return -ENOMEM; + } + + bf->bf_buf_addr = bf->bf_dmacontext; + return 0; +} + +/* FIXME: tx power */ +static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf, + struct ath_tx_control *txctl) +{ + struct sk_buff *skb = bf->bf_mpdu; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ath_node *an = NULL; + struct list_head bf_head; + struct ath_desc *ds; + struct ath_atx_tid *tid; + struct ath_hw *ah = sc->sc_ah; + int frm_type; + __le16 fc; + + frm_type = get_hw_packet_type(skb); + fc = hdr->frame_control; + + INIT_LIST_HEAD(&bf_head); + list_add_tail(&bf->list, &bf_head); + + ds = bf->bf_desc; + ds->ds_link = 0; + ds->ds_data = bf->bf_buf_addr; + + ath9k_hw_set11n_txdesc(ah, ds, bf->bf_frmlen, frm_type, MAX_RATE_POWER, + bf->bf_keyix, bf->bf_keytype, bf->bf_flags); + + ath9k_hw_filltxdesc(ah, ds, + skb->len, /* segment length */ + true, /* first segment */ + true, /* last segment */ + ds); /* first descriptor */ + + spin_lock_bh(&txctl->txq->axq_lock); + + if (bf_isht(bf) && (sc->sc_flags & SC_OP_TXAGGR) && + tx_info->control.sta) { + an = (struct ath_node *)tx_info->control.sta->drv_priv; + tid = ATH_AN_2_TID(an, bf->bf_tidno); + + if (!ieee80211_is_data_qos(fc)) { + ath_tx_send_normal(sc, txctl->txq, &bf_head); + goto tx_done; + } + + if (ath_aggr_query(sc, an, bf->bf_tidno)) { + /* + * Try aggregation if it's a unicast data frame + * and the destination is HT capable. + */ + ath_tx_send_ampdu(sc, tid, &bf_head, txctl); + } else { + /* + * Send this frame as regular when ADDBA + * exchange is neither complete nor pending. + */ + ath_tx_send_ht_normal(sc, txctl->txq, + tid, &bf_head); + } + } else { + ath_tx_send_normal(sc, txctl->txq, &bf_head); + } + +tx_done: + spin_unlock_bh(&txctl->txq->axq_lock); +} + +/* Upon failure caller should free skb */ +int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath_tx_control *txctl) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_buf *bf; + int r; + + bf = ath_tx_get_buffer(sc); + if (!bf) { + DPRINTF(sc, ATH_DBG_XMIT, "TX buffers are full\n"); + return -1; + } + + r = ath_tx_setup_buffer(hw, bf, skb, txctl); + if (unlikely(r)) { + struct ath_txq *txq = txctl->txq; + + DPRINTF(sc, ATH_DBG_FATAL, "TX mem alloc failure\n"); + + /* upon ath_tx_processq() this TX queue will be resumed, we + * guarantee this will happen by knowing beforehand that + * we will at least have to run TX completionon one buffer + * on the queue */ + spin_lock_bh(&txq->axq_lock); + if (sc->tx.txq[txq->axq_qnum].axq_depth > 1) { + ieee80211_stop_queue(sc->hw, + skb_get_queue_mapping(skb)); + txq->stopped = 1; + } + spin_unlock_bh(&txq->axq_lock); + + spin_lock_bh(&sc->tx.txbuflock); + list_add_tail(&bf->list, &sc->tx.txbuf); + spin_unlock_bh(&sc->tx.txbuflock); + + return r; + } + + ath_tx_start_dma(sc, bf, txctl); + + return 0; +} + +void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + int hdrlen, padsize; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ath_tx_control txctl; + + memset(&txctl, 0, sizeof(struct ath_tx_control)); + + /* + * As a temporary workaround, assign seq# here; this will likely need + * to be cleaned up to work better with Beacon transmission and virtual + * BSSes. + */ + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + sc->tx.seq_no += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); + } + + /* Add the padding after the header if this is not already done */ + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + if (hdrlen & 3) { + padsize = hdrlen % 4; + if (skb_headroom(skb) < padsize) { + DPRINTF(sc, ATH_DBG_XMIT, "TX CABQ padding failed\n"); + dev_kfree_skb_any(skb); + return; + } + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, hdrlen); + } + + txctl.txq = sc->beacon.cabq; + + DPRINTF(sc, ATH_DBG_XMIT, "transmitting CABQ packet, skb: %p\n", skb); + + if (ath_tx_start(hw, skb, &txctl) != 0) { + DPRINTF(sc, ATH_DBG_XMIT, "CABQ TX failed\n"); + goto exit; + } + + return; +exit: + dev_kfree_skb_any(skb); +} + +/*****************/ +/* TX Completion */ +/*****************/ + +static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, + int tx_flags) +{ + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); + int hdrlen, padsize; + int frame_type = ATH9K_NOT_INTERNAL; + + DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb); + + if (tx_info_priv) { + hw = tx_info_priv->aphy->hw; + frame_type = tx_info_priv->frame_type; + } + + if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK || + tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) { + kfree(tx_info_priv); + tx_info->rate_driver_data[0] = NULL; + } + + if (tx_flags & ATH_TX_BAR) + tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; + + if (!(tx_flags & (ATH_TX_ERROR | ATH_TX_XRETRY))) { + /* Frame was ACKed */ + tx_info->flags |= IEEE80211_TX_STAT_ACK; + } + + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + padsize = hdrlen & 3; + if (padsize && hdrlen >= 24) { + /* + * Remove MAC header padding before giving the frame back to + * mac80211. + */ + memmove(skb->data + padsize, skb->data, hdrlen); + skb_pull(skb, padsize); + } + + if (sc->sc_flags & SC_OP_WAIT_FOR_TX_ACK) { + sc->sc_flags &= ~SC_OP_WAIT_FOR_TX_ACK; + DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having " + "received TX status (0x%x)\n", + sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | + SC_OP_WAIT_FOR_CAB | + SC_OP_WAIT_FOR_PSPOLL_DATA | + SC_OP_WAIT_FOR_TX_ACK)); + } + + if (frame_type == ATH9K_NOT_INTERNAL) + ieee80211_tx_status(hw, skb); + else + ath9k_tx_status(hw, skb); +} + +static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, + struct list_head *bf_q, + int txok, int sendbar) +{ + struct sk_buff *skb = bf->bf_mpdu; + unsigned long flags; + int tx_flags = 0; + + + if (sendbar) + tx_flags = ATH_TX_BAR; + + if (!txok) { + tx_flags |= ATH_TX_ERROR; + + if (bf_isxretried(bf)) + tx_flags |= ATH_TX_XRETRY; + } + + dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE); + ath_tx_complete(sc, skb, tx_flags); + + /* + * Return the list of ath_buf of this mpdu to free queue + */ + spin_lock_irqsave(&sc->tx.txbuflock, flags); + list_splice_tail_init(bf_q, &sc->tx.txbuf); + spin_unlock_irqrestore(&sc->tx.txbuflock, flags); +} + +static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf, + int txok) +{ + struct ath_buf *bf_last = bf->bf_lastbf; + struct ath_desc *ds = bf_last->bf_desc; + u16 seq_st = 0; + u32 ba[WME_BA_BMP_SIZE >> 5]; + int ba_index; + int nbad = 0; + int isaggr = 0; + + if (ds->ds_txstat.ts_flags == ATH9K_TX_SW_ABORTED) + return 0; + + isaggr = bf_isaggr(bf); + if (isaggr) { + seq_st = ATH_DS_BA_SEQ(ds); + memcpy(ba, ATH_DS_BA_BITMAP(ds), WME_BA_BMP_SIZE >> 3); + } + + while (bf) { + ba_index = ATH_BA_INDEX(seq_st, bf->bf_seqno); + if (!txok || (isaggr && !ATH_BA_ISSET(ba, ba_index))) + nbad++; + + bf = bf->bf_next; + } + + return nbad; +} + +static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, + int nbad, int txok, bool update_rc) +{ + struct sk_buff *skb = bf->bf_mpdu; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); + struct ieee80211_hw *hw = tx_info_priv->aphy->hw; + u8 i, tx_rateindex; + + if (txok) + tx_info->status.ack_signal = ds->ds_txstat.ts_rssi; + + tx_rateindex = ds->ds_txstat.ts_rateindex; + WARN_ON(tx_rateindex >= hw->max_rates); + + tx_info_priv->update_rc = update_rc; + if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) + tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + + if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 && + (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) { + if (ieee80211_is_data(hdr->frame_control)) { + memcpy(&tx_info_priv->tx, &ds->ds_txstat, + sizeof(tx_info_priv->tx)); + tx_info_priv->n_frames = bf->bf_nframes; + tx_info_priv->n_bad_frames = nbad; + } + } + + for (i = tx_rateindex + 1; i < hw->max_rates; i++) + tx_info->status.rates[i].count = 0; + + tx_info->status.rates[tx_rateindex].count = bf->bf_retries + 1; +} + +static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq) +{ + int qnum; + + spin_lock_bh(&txq->axq_lock); + if (txq->stopped && + sc->tx.txq[txq->axq_qnum].axq_depth <= (ATH_TXBUF - 20)) { + qnum = ath_get_mac80211_qnum(txq->axq_qnum, sc); + if (qnum != -1) { + ieee80211_wake_queue(sc->hw, qnum); + txq->stopped = 0; + } + } + spin_unlock_bh(&txq->axq_lock); +} + +static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_buf *bf, *lastbf, *bf_held = NULL; + struct list_head bf_head; + struct ath_desc *ds; + int txok; + int status; + + DPRINTF(sc, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n", + txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum), + txq->axq_link); + + for (;;) { + spin_lock_bh(&txq->axq_lock); + if (list_empty(&txq->axq_q)) { + txq->axq_link = NULL; + txq->axq_linkbuf = NULL; + spin_unlock_bh(&txq->axq_lock); + break; + } + bf = list_first_entry(&txq->axq_q, struct ath_buf, list); + + /* + * There is a race condition that a BH gets scheduled + * after sw writes TxE and before hw re-load the last + * descriptor to get the newly chained one. + * Software must keep the last DONE descriptor as a + * holding descriptor - software does so by marking + * it with the STALE flag. + */ + bf_held = NULL; + if (bf->bf_stale) { + bf_held = bf; + if (list_is_last(&bf_held->list, &txq->axq_q)) { + txq->axq_link = NULL; + txq->axq_linkbuf = NULL; + spin_unlock_bh(&txq->axq_lock); + + /* + * The holding descriptor is the last + * descriptor in queue. It's safe to remove + * the last holding descriptor in BH context. + */ + spin_lock_bh(&sc->tx.txbuflock); + list_move_tail(&bf_held->list, &sc->tx.txbuf); + spin_unlock_bh(&sc->tx.txbuflock); + + break; + } else { + bf = list_entry(bf_held->list.next, + struct ath_buf, list); + } + } + + lastbf = bf->bf_lastbf; + ds = lastbf->bf_desc; + + status = ath9k_hw_txprocdesc(ah, ds); + if (status == -EINPROGRESS) { + spin_unlock_bh(&txq->axq_lock); + break; + } + if (bf->bf_desc == txq->axq_lastdsWithCTS) + txq->axq_lastdsWithCTS = NULL; + if (ds == txq->axq_gatingds) + txq->axq_gatingds = NULL; + + /* + * Remove ath_buf's of the same transmit unit from txq, + * however leave the last descriptor back as the holding + * descriptor for hw. + */ + lastbf->bf_stale = true; + INIT_LIST_HEAD(&bf_head); + if (!list_is_singular(&lastbf->list)) + list_cut_position(&bf_head, + &txq->axq_q, lastbf->list.prev); + + txq->axq_depth--; + if (bf_isaggr(bf)) + txq->axq_aggr_depth--; + + txok = (ds->ds_txstat.ts_status == 0); + spin_unlock_bh(&txq->axq_lock); + + if (bf_held) { + spin_lock_bh(&sc->tx.txbuflock); + list_move_tail(&bf_held->list, &sc->tx.txbuf); + spin_unlock_bh(&sc->tx.txbuflock); + } + + if (!bf_isampdu(bf)) { + /* + * This frame is sent out as a single frame. + * Use hardware retry status for this frame. + */ + bf->bf_retries = ds->ds_txstat.ts_longretry; + if (ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY) + bf->bf_state.bf_type |= BUF_XRETRY; + ath_tx_rc_status(bf, ds, 0, txok, true); + } + + if (bf_isampdu(bf)) + ath_tx_complete_aggr(sc, txq, bf, &bf_head, txok); + else + ath_tx_complete_buf(sc, bf, &bf_head, txok, 0); + + ath_wake_mac80211_queue(sc, txq); + + spin_lock_bh(&txq->axq_lock); + if (sc->sc_flags & SC_OP_TXAGGR) + ath_txq_schedule(sc, txq); + spin_unlock_bh(&txq->axq_lock); + } +} + + +void ath_tx_tasklet(struct ath_softc *sc) +{ + int i; + u32 qcumask = ((1 << ATH9K_NUM_TX_QUEUES) - 1); + + ath9k_hw_gettxintrtxqs(sc->sc_ah, &qcumask); + + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i) && (qcumask & (1 << i))) + ath_tx_processq(sc, &sc->tx.txq[i]); + } +} + +/*****************/ +/* Init, Cleanup */ +/*****************/ + +int ath_tx_init(struct ath_softc *sc, int nbufs) +{ + int error = 0; + + spin_lock_init(&sc->tx.txbuflock); + + error = ath_descdma_setup(sc, &sc->tx.txdma, &sc->tx.txbuf, + "tx", nbufs, 1); + if (error != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "Failed to allocate tx descriptors: %d\n", error); + goto err; + } + + error = ath_descdma_setup(sc, &sc->beacon.bdma, &sc->beacon.bbuf, + "beacon", ATH_BCBUF, 1); + if (error != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "Failed to allocate beacon descriptors: %d\n", error); + goto err; + } + +err: + if (error != 0) + ath_tx_cleanup(sc); + + return error; +} + +void ath_tx_cleanup(struct ath_softc *sc) +{ + if (sc->beacon.bdma.dd_desc_len != 0) + ath_descdma_cleanup(sc, &sc->beacon.bdma, &sc->beacon.bbuf); + + if (sc->tx.txdma.dd_desc_len != 0) + ath_descdma_cleanup(sc, &sc->tx.txdma, &sc->tx.txbuf); +} + +void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) +{ + struct ath_atx_tid *tid; + struct ath_atx_ac *ac; + int tidno, acno; + + for (tidno = 0, tid = &an->tid[tidno]; + tidno < WME_NUM_TID; + tidno++, tid++) { + tid->an = an; + tid->tidno = tidno; + tid->seq_start = tid->seq_next = 0; + tid->baw_size = WME_MAX_BA; + tid->baw_head = tid->baw_tail = 0; + tid->sched = false; + tid->paused = false; + tid->state &= ~AGGR_CLEANUP; + INIT_LIST_HEAD(&tid->buf_q); + acno = TID_TO_WME_AC(tidno); + tid->ac = &an->ac[acno]; + tid->state &= ~AGGR_ADDBA_COMPLETE; + tid->state &= ~AGGR_ADDBA_PROGRESS; + tid->addba_exchangeattempts = 0; + } + + for (acno = 0, ac = &an->ac[acno]; + acno < WME_NUM_AC; acno++, ac++) { + ac->sched = false; + INIT_LIST_HEAD(&ac->tid_q); + + switch (acno) { + case WME_AC_BE: + ac->qnum = ath_tx_get_qnum(sc, + ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE); + break; + case WME_AC_BK: + ac->qnum = ath_tx_get_qnum(sc, + ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BK); + break; + case WME_AC_VI: + ac->qnum = ath_tx_get_qnum(sc, + ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VI); + break; + case WME_AC_VO: + ac->qnum = ath_tx_get_qnum(sc, + ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VO); + break; + } + } +} + +void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) +{ + int i; + struct ath_atx_ac *ac, *ac_tmp; + struct ath_atx_tid *tid, *tid_tmp; + struct ath_txq *txq; + + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + txq = &sc->tx.txq[i]; + + spin_lock(&txq->axq_lock); + + list_for_each_entry_safe(ac, + ac_tmp, &txq->axq_acq, list) { + tid = list_first_entry(&ac->tid_q, + struct ath_atx_tid, list); + if (tid && tid->an != an) + continue; + list_del(&ac->list); + ac->sched = false; + + list_for_each_entry_safe(tid, + tid_tmp, &ac->tid_q, list) { + list_del(&tid->list); + tid->sched = false; + ath_tid_drain(sc, txq, tid); + tid->state &= ~AGGR_ADDBA_COMPLETE; + tid->addba_exchangeattempts = 0; + tid->state &= ~AGGR_CLEANUP; + } + } + + spin_unlock(&txq->axq_lock); + } + } +} diff --git a/drivers/net/wireless/ath/main.c b/drivers/net/wireless/ath/main.c new file mode 100644 index 00000000000..9949b11cb15 --- /dev/null +++ b/drivers/net/wireless/ath/main.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2009 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 <linux/kernel.h> +#include <linux/module.h> + +MODULE_AUTHOR("Atheros Communications"); +MODULE_DESCRIPTION("Shared library for Atheros wireless LAN cards."); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c new file mode 100644 index 00000000000..eef370bd121 --- /dev/null +++ b/drivers/net/wireless/ath/regd.c @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2008-2009 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 <linux/kernel.h> +#include <linux/slab.h> +#include <net/cfg80211.h> +#include <net/mac80211.h> +#include "regd.h" +#include "regd_common.h" + +/* + * This is a set of common rules used by our world regulatory domains. + * We have 12 world regulatory domains. To save space we consolidate + * the regulatory domains in 5 structures by frequency and change + * the flags on our reg_notifier() on a case by case basis. + */ + +/* Only these channels all allow active scan on all world regulatory domains */ +#define ATH9K_2GHZ_CH01_11 REG_RULE(2412-10, 2462+10, 40, 0, 20, 0) + +/* We enable active scan on these a case by case basis by regulatory domain */ +#define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\ + NL80211_RRF_PASSIVE_SCAN) +#define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\ + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM) + +/* We allow IBSS on these on a case by case basis by regulatory domain */ +#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 40, 0, 30,\ + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) +#define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 40, 0, 30,\ + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) +#define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 40, 0, 30,\ + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) + +#define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \ + ATH9K_2GHZ_CH12_13, \ + ATH9K_2GHZ_CH14 + +#define ATH9K_5GHZ_ALL ATH9K_5GHZ_5150_5350, \ + ATH9K_5GHZ_5470_5850 +/* This one skips what we call "mid band" */ +#define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5150_5350, \ + ATH9K_5GHZ_5725_5850 + +/* Can be used for: + * 0x60, 0x61, 0x62 */ +static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = { + .n_reg_rules = 5, + .alpha2 = "99", + .reg_rules = { + ATH9K_2GHZ_ALL, + ATH9K_5GHZ_ALL, + } +}; + +/* Can be used by 0x63 and 0x65 */ +static const struct ieee80211_regdomain ath_world_regdom_63_65 = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + ATH9K_2GHZ_CH01_11, + ATH9K_2GHZ_CH12_13, + ATH9K_5GHZ_NO_MIDBAND, + } +}; + +/* Can be used by 0x64 only */ +static const struct ieee80211_regdomain ath_world_regdom_64 = { + .n_reg_rules = 3, + .alpha2 = "99", + .reg_rules = { + ATH9K_2GHZ_CH01_11, + ATH9K_5GHZ_NO_MIDBAND, + } +}; + +/* Can be used by 0x66 and 0x69 */ +static const struct ieee80211_regdomain ath_world_regdom_66_69 = { + .n_reg_rules = 3, + .alpha2 = "99", + .reg_rules = { + ATH9K_2GHZ_CH01_11, + ATH9K_5GHZ_ALL, + } +}; + +/* Can be used by 0x67, 0x6A and 0x68 */ +static const struct ieee80211_regdomain ath_world_regdom_67_68_6A = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + ATH9K_2GHZ_CH01_11, + ATH9K_2GHZ_CH12_13, + ATH9K_5GHZ_ALL, + } +}; + +static inline bool is_wwr_sku(u16 regd) +{ + return ((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) || + (regd == WORLD); +} + +static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg) +{ + return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG; +} + +bool ath_is_world_regd(struct ath_regulatory *reg) +{ + return is_wwr_sku(ath_regd_get_eepromRD(reg)); +} +EXPORT_SYMBOL(ath_is_world_regd); + +static const struct ieee80211_regdomain *ath_default_world_regdomain(void) +{ + /* this is the most restrictive */ + return &ath_world_regdom_64; +} + +static const struct +ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg) +{ + switch (reg->regpair->regDmnEnum) { + case 0x60: + case 0x61: + case 0x62: + return &ath_world_regdom_60_61_62; + case 0x63: + case 0x65: + return &ath_world_regdom_63_65; + case 0x64: + return &ath_world_regdom_64; + case 0x66: + case 0x69: + return &ath_world_regdom_66_69; + case 0x67: + case 0x68: + case 0x6A: + return &ath_world_regdom_67_68_6A; + default: + WARN_ON(1); + return ath_default_world_regdomain(); + } +} + +/* Frequency is one where radar detection is required */ +static bool ath_is_radar_freq(u16 center_freq) +{ + return (center_freq >= 5260 && center_freq <= 5700); +} + +/* + * N.B: These exception rules do not apply radar freqs. + * + * - We enable adhoc (or beaconing) if allowed by 11d + * - We enable active scan if the channel is allowed by 11d + * - If no country IE has been processed and a we determine we have + * received a beacon on a channel we can enable active scan and + * adhoc (or beaconing). + */ +static void +ath_reg_apply_beaconing_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) +{ + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + const struct ieee80211_reg_rule *reg_rule; + struct ieee80211_channel *ch; + unsigned int i; + u32 bandwidth = 0; + int r; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + + if (!wiphy->bands[band]) + continue; + + sband = wiphy->bands[band]; + + for (i = 0; i < sband->n_channels; i++) { + + ch = &sband->channels[i]; + + if (ath_is_radar_freq(ch->center_freq) || + (ch->flags & IEEE80211_CHAN_RADAR)) + continue; + + if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { + r = freq_reg_info(wiphy, + ch->center_freq, + bandwidth, + ®_rule); + if (r) + continue; + /* + * If 11d had a rule for this channel ensure + * we enable adhoc/beaconing if it allows us to + * use it. Note that we would have disabled it + * by applying our static world regdomain by + * default during init, prior to calling our + * regulatory_hint(). + */ + if (!(reg_rule->flags & + NL80211_RRF_NO_IBSS)) + ch->flags &= + ~IEEE80211_CHAN_NO_IBSS; + if (!(reg_rule->flags & + NL80211_RRF_PASSIVE_SCAN)) + ch->flags &= + ~IEEE80211_CHAN_PASSIVE_SCAN; + } else { + if (ch->beacon_found) + ch->flags &= ~(IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_PASSIVE_SCAN); + } + } + } + +} + +/* Allows active scan scan on Ch 12 and 13 */ +static void +ath_reg_apply_active_scan_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + const struct ieee80211_reg_rule *reg_rule; + u32 bandwidth = 0; + int r; + + sband = wiphy->bands[IEEE80211_BAND_2GHZ]; + + /* + * If no country IE has been received always enable active scan + * on these channels. This is only done for specific regulatory SKUs + */ + if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { + ch = &sband->channels[11]; /* CH 12 */ + if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + ch = &sband->channels[12]; /* CH 13 */ + if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + return; + } + + /* + * If a country IE has been recieved check its rule for this + * channel first before enabling active scan. The passive scan + * would have been enforced by the initial processing of our + * custom regulatory domain. + */ + + ch = &sband->channels[11]; /* CH 12 */ + r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + if (!r) { + if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) + if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + } + + ch = &sband->channels[12]; /* CH 13 */ + r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + if (!r) { + if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) + if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + } +} + +/* Always apply Radar/DFS rules on freq range 5260 MHz - 5700 MHz */ +static void ath_reg_apply_radar_flags(struct wiphy *wiphy) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + unsigned int i; + + if (!wiphy->bands[IEEE80211_BAND_5GHZ]) + return; + + sband = wiphy->bands[IEEE80211_BAND_5GHZ]; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + if (!ath_is_radar_freq(ch->center_freq)) + continue; + /* We always enable radar detection/DFS on this + * frequency range. Additionally we also apply on + * this frequency range: + * - If STA mode does not yet have DFS supports disable + * active scanning + * - If adhoc mode does not support DFS yet then + * disable adhoc in the frequency. + * - If AP mode does not yet support radar detection/DFS + * do not allow AP mode + */ + if (!(ch->flags & IEEE80211_CHAN_DISABLED)) + ch->flags |= IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_PASSIVE_SCAN; + } +} + +static void ath_reg_apply_world_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator, + struct ath_regulatory *reg) +{ + switch (reg->regpair->regDmnEnum) { + case 0x60: + case 0x63: + case 0x66: + case 0x67: + ath_reg_apply_beaconing_flags(wiphy, initiator); + break; + case 0x68: + ath_reg_apply_beaconing_flags(wiphy, initiator); + ath_reg_apply_active_scan_flags(wiphy, initiator); + break; + } + return; +} + +int ath_reg_notifier_apply(struct wiphy *wiphy, + struct regulatory_request *request, + struct ath_regulatory *reg) +{ + /* We always apply this */ + ath_reg_apply_radar_flags(wiphy); + + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_DRIVER: + case NL80211_REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_USER: + break; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + if (ath_is_world_regd(reg)) + ath_reg_apply_world_flags(wiphy, request->initiator, + reg); + break; + } + + return 0; +} +EXPORT_SYMBOL(ath_reg_notifier_apply); + +static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg) +{ + u16 rd = ath_regd_get_eepromRD(reg); + int i; + + if (rd & COUNTRY_ERD_FLAG) { + /* EEPROM value is a country code */ + u16 cc = rd & ~COUNTRY_ERD_FLAG; + printk(KERN_DEBUG + "ath: EEPROM indicates we should expect " + "a country code\n"); + for (i = 0; i < ARRAY_SIZE(allCountries); i++) + if (allCountries[i].countryCode == cc) + return true; + } else { + /* EEPROM value is a regpair value */ + if (rd != CTRY_DEFAULT) + printk(KERN_DEBUG "ath: EEPROM indicates we " + "should expect a direct regpair map\n"); + for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) + if (regDomainPairs[i].regDmnEnum == rd) + return true; + } + printk(KERN_DEBUG + "ath: invalid regulatory domain/country code 0x%x\n", rd); + return false; +} + +/* EEPROM country code to regpair mapping */ +static struct country_code_to_enum_rd* +ath_regd_find_country(u16 countryCode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(allCountries); i++) { + if (allCountries[i].countryCode == countryCode) + return &allCountries[i]; + } + return NULL; +} + +/* EEPROM rd code to regpair mapping */ +static struct country_code_to_enum_rd* +ath_regd_find_country_by_rd(int regdmn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(allCountries); i++) { + if (allCountries[i].regDmnEnum == regdmn) + return &allCountries[i]; + } + return NULL; +} + +/* Returns the map of the EEPROM set RD to a country code */ +static u16 ath_regd_get_default_country(u16 rd) +{ + if (rd & COUNTRY_ERD_FLAG) { + struct country_code_to_enum_rd *country = NULL; + u16 cc = rd & ~COUNTRY_ERD_FLAG; + + country = ath_regd_find_country(cc); + if (country != NULL) + return cc; + } + + return CTRY_DEFAULT; +} + +static struct reg_dmn_pair_mapping* +ath_get_regpair(int regdmn) +{ + int i; + + if (regdmn == NO_ENUMRD) + return NULL; + for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { + if (regDomainPairs[i].regDmnEnum == regdmn) + return ®DomainPairs[i]; + } + return NULL; +} + +static int +ath_regd_init_wiphy(struct ath_regulatory *reg, + struct wiphy *wiphy, + int (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request)) +{ + const struct ieee80211_regdomain *regd; + + wiphy->reg_notifier = reg_notifier; + wiphy->strict_regulatory = true; + + if (ath_is_world_regd(reg)) { + /* + * Anything applied here (prior to wiphy registration) gets + * saved on the wiphy orig_* parameters + */ + regd = ath_world_regdomain(reg); + wiphy->custom_regulatory = true; + wiphy->strict_regulatory = false; + } else { + /* + * This gets applied in the case of the absense of CRDA, + * it's our own custom world regulatory domain, similar to + * cfg80211's but we enable passive scanning. + */ + regd = ath_default_world_regdomain(); + } + wiphy_apply_custom_regulatory(wiphy, regd); + ath_reg_apply_radar_flags(wiphy); + ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg); + return 0; +} + +int +ath_regd_init(struct ath_regulatory *reg, + struct wiphy *wiphy, + int (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request)) +{ + struct country_code_to_enum_rd *country = NULL; + u16 regdmn; + + if (!reg) + return -EINVAL; + + printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd); + + if (!ath_regd_is_eeprom_valid(reg)) { + printk(KERN_ERR "ath: Invalid EEPROM contents\n"); + return -EINVAL; + } + + regdmn = ath_regd_get_eepromRD(reg); + reg->country_code = ath_regd_get_default_country(regdmn); + + if (reg->country_code == CTRY_DEFAULT && + regdmn == CTRY_DEFAULT) { + printk(KERN_DEBUG "ath: EEPROM indicates default " + "country code should be used\n"); + reg->country_code = CTRY_UNITED_STATES; + } + + if (reg->country_code == CTRY_DEFAULT) { + country = NULL; + } else { + printk(KERN_DEBUG "ath: doing EEPROM country->regdmn " + "map search\n"); + country = ath_regd_find_country(reg->country_code); + if (country == NULL) { + printk(KERN_DEBUG + "ath: no valid country maps found for " + "country code: 0x%0x\n", + reg->country_code); + return -EINVAL; + } else { + regdmn = country->regDmnEnum; + printk(KERN_DEBUG "ath: country maps to " + "regdmn code: 0x%0x\n", + regdmn); + } + } + + reg->regpair = ath_get_regpair(regdmn); + + if (!reg->regpair) { + printk(KERN_DEBUG "ath: " + "No regulatory domain pair found, cannot continue\n"); + return -EINVAL; + } + + if (!country) + country = ath_regd_find_country_by_rd(regdmn); + + if (country) { + reg->alpha2[0] = country->isoName[0]; + reg->alpha2[1] = country->isoName[1]; + } else { + reg->alpha2[0] = '0'; + reg->alpha2[1] = '0'; + } + + printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n", + reg->alpha2[0], reg->alpha2[1]); + printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n", + reg->regpair->regDmnEnum); + + ath_regd_init_wiphy(reg, wiphy, reg_notifier); + return 0; +} +EXPORT_SYMBOL(ath_regd_init); + +u32 ath_regd_get_band_ctl(struct ath_regulatory *reg, + enum ieee80211_band band) +{ + if (!reg->regpair || + (reg->country_code == CTRY_DEFAULT && + is_wwr_sku(ath_regd_get_eepromRD(reg)))) { + return SD_NO_CTL; + } + + switch (band) { + case IEEE80211_BAND_2GHZ: + return reg->regpair->reg_2ghz_ctl; + case IEEE80211_BAND_5GHZ: + return reg->regpair->reg_5ghz_ctl; + default: + return NO_CTL; + } + + return NO_CTL; +} +EXPORT_SYMBOL(ath_regd_get_band_ctl); diff --git a/drivers/net/wireless/ath/regd.h b/drivers/net/wireless/ath/regd.h new file mode 100644 index 00000000000..07291ccb23f --- /dev/null +++ b/drivers/net/wireless/ath/regd.h @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2008-2009 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 REGD_H +#define REGD_H + +#include <linux/nl80211.h> + +#include <net/cfg80211.h> + +#define NO_CTL 0xff +#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 CTRY_DEBUG 0x1ff +#define CTRY_DEFAULT 0 + +#define COUNTRY_ERD_FLAG 0x8000 +#define WORLDWIDE_ROAMING_FLAG 0x4000 + +#define MULTI_DOMAIN_MASK 0xFF00 + +#define WORLD_SKU_MASK 0x00F0 +#define WORLD_SKU_PREFIX 0x0060 + +#define CHANNEL_HALF_BW 10 +#define CHANNEL_QUARTER_BW 5 + +struct reg_dmn_pair_mapping { + u16 regDmnEnum; + u16 reg_5ghz_ctl; + u16 reg_2ghz_ctl; +}; + +struct country_code_to_enum_rd { + u16 countryCode; + u16 regDmnEnum; + const char *isoName; +}; + +struct ath_regulatory { + char alpha2[2]; + u16 country_code; + u16 max_power_level; + u32 tp_scale; + u16 current_rd; + u16 current_rd_ext; + int16_t power_limit; + struct reg_dmn_pair_mapping *regpair; +}; + +enum CountryCode { + CTRY_ALBANIA = 8, + CTRY_ALGERIA = 12, + CTRY_ARGENTINA = 32, + CTRY_ARMENIA = 51, + CTRY_AUSTRALIA = 36, + CTRY_AUSTRIA = 40, + CTRY_AZERBAIJAN = 31, + CTRY_BAHRAIN = 48, + CTRY_BELARUS = 112, + CTRY_BELGIUM = 56, + CTRY_BELIZE = 84, + CTRY_BOLIVIA = 68, + CTRY_BOSNIA_HERZ = 70, + CTRY_BRAZIL = 76, + CTRY_BRUNEI_DARUSSALAM = 96, + CTRY_BULGARIA = 100, + CTRY_CANADA = 124, + CTRY_CHILE = 152, + CTRY_CHINA = 156, + CTRY_COLOMBIA = 170, + CTRY_COSTA_RICA = 188, + CTRY_CROATIA = 191, + CTRY_CYPRUS = 196, + CTRY_CZECH = 203, + CTRY_DENMARK = 208, + CTRY_DOMINICAN_REPUBLIC = 214, + CTRY_ECUADOR = 218, + CTRY_EGYPT = 818, + CTRY_EL_SALVADOR = 222, + CTRY_ESTONIA = 233, + CTRY_FAEROE_ISLANDS = 234, + CTRY_FINLAND = 246, + CTRY_FRANCE = 250, + CTRY_GEORGIA = 268, + CTRY_GERMANY = 276, + CTRY_GREECE = 300, + CTRY_GUATEMALA = 320, + CTRY_HONDURAS = 340, + CTRY_HONG_KONG = 344, + CTRY_HUNGARY = 348, + CTRY_ICELAND = 352, + CTRY_INDIA = 356, + CTRY_INDONESIA = 360, + CTRY_IRAN = 364, + CTRY_IRAQ = 368, + CTRY_IRELAND = 372, + CTRY_ISRAEL = 376, + CTRY_ITALY = 380, + CTRY_JAMAICA = 388, + CTRY_JAPAN = 392, + CTRY_JORDAN = 400, + CTRY_KAZAKHSTAN = 398, + CTRY_KENYA = 404, + CTRY_KOREA_NORTH = 408, + CTRY_KOREA_ROC = 410, + CTRY_KOREA_ROC2 = 411, + CTRY_KOREA_ROC3 = 412, + CTRY_KUWAIT = 414, + CTRY_LATVIA = 428, + CTRY_LEBANON = 422, + CTRY_LIBYA = 434, + CTRY_LIECHTENSTEIN = 438, + CTRY_LITHUANIA = 440, + CTRY_LUXEMBOURG = 442, + CTRY_MACAU = 446, + CTRY_MACEDONIA = 807, + CTRY_MALAYSIA = 458, + CTRY_MALTA = 470, + CTRY_MEXICO = 484, + CTRY_MONACO = 492, + CTRY_MOROCCO = 504, + CTRY_NEPAL = 524, + CTRY_NETHERLANDS = 528, + CTRY_NETHERLANDS_ANTILLES = 530, + CTRY_NEW_ZEALAND = 554, + CTRY_NICARAGUA = 558, + CTRY_NORWAY = 578, + CTRY_OMAN = 512, + CTRY_PAKISTAN = 586, + CTRY_PANAMA = 591, + CTRY_PAPUA_NEW_GUINEA = 598, + CTRY_PARAGUAY = 600, + CTRY_PERU = 604, + CTRY_PHILIPPINES = 608, + CTRY_POLAND = 616, + CTRY_PORTUGAL = 620, + CTRY_PUERTO_RICO = 630, + CTRY_QATAR = 634, + CTRY_ROMANIA = 642, + CTRY_RUSSIA = 643, + CTRY_SAUDI_ARABIA = 682, + CTRY_SERBIA_MONTENEGRO = 891, + CTRY_SINGAPORE = 702, + CTRY_SLOVAKIA = 703, + CTRY_SLOVENIA = 705, + CTRY_SOUTH_AFRICA = 710, + CTRY_SPAIN = 724, + CTRY_SRI_LANKA = 144, + CTRY_SWEDEN = 752, + CTRY_SWITZERLAND = 756, + CTRY_SYRIA = 760, + CTRY_TAIWAN = 158, + CTRY_THAILAND = 764, + CTRY_TRINIDAD_Y_TOBAGO = 780, + CTRY_TUNISIA = 788, + CTRY_TURKEY = 792, + CTRY_UAE = 784, + CTRY_UKRAINE = 804, + CTRY_UNITED_KINGDOM = 826, + CTRY_UNITED_STATES = 840, + CTRY_UNITED_STATES_FCC49 = 842, + CTRY_URUGUAY = 858, + CTRY_UZBEKISTAN = 860, + CTRY_VENEZUELA = 862, + CTRY_VIET_NAM = 704, + CTRY_YEMEN = 887, + CTRY_ZIMBABWE = 716, + CTRY_JAPAN1 = 393, + CTRY_JAPAN2 = 394, + CTRY_JAPAN3 = 395, + CTRY_JAPAN4 = 396, + CTRY_JAPAN5 = 397, + CTRY_JAPAN6 = 4006, + CTRY_JAPAN7 = 4007, + CTRY_JAPAN8 = 4008, + CTRY_JAPAN9 = 4009, + CTRY_JAPAN10 = 4010, + CTRY_JAPAN11 = 4011, + CTRY_JAPAN12 = 4012, + CTRY_JAPAN13 = 4013, + CTRY_JAPAN14 = 4014, + CTRY_JAPAN15 = 4015, + CTRY_JAPAN16 = 4016, + CTRY_JAPAN17 = 4017, + CTRY_JAPAN18 = 4018, + CTRY_JAPAN19 = 4019, + CTRY_JAPAN20 = 4020, + CTRY_JAPAN21 = 4021, + CTRY_JAPAN22 = 4022, + CTRY_JAPAN23 = 4023, + CTRY_JAPAN24 = 4024, + CTRY_JAPAN25 = 4025, + CTRY_JAPAN26 = 4026, + CTRY_JAPAN27 = 4027, + CTRY_JAPAN28 = 4028, + CTRY_JAPAN29 = 4029, + CTRY_JAPAN30 = 4030, + CTRY_JAPAN31 = 4031, + CTRY_JAPAN32 = 4032, + CTRY_JAPAN33 = 4033, + CTRY_JAPAN34 = 4034, + CTRY_JAPAN35 = 4035, + CTRY_JAPAN36 = 4036, + CTRY_JAPAN37 = 4037, + CTRY_JAPAN38 = 4038, + CTRY_JAPAN39 = 4039, + CTRY_JAPAN40 = 4040, + CTRY_JAPAN41 = 4041, + CTRY_JAPAN42 = 4042, + CTRY_JAPAN43 = 4043, + CTRY_JAPAN44 = 4044, + CTRY_JAPAN45 = 4045, + CTRY_JAPAN46 = 4046, + CTRY_JAPAN47 = 4047, + CTRY_JAPAN48 = 4048, + CTRY_JAPAN49 = 4049, + CTRY_JAPAN50 = 4050, + CTRY_JAPAN51 = 4051, + CTRY_JAPAN52 = 4052, + CTRY_JAPAN53 = 4053, + CTRY_JAPAN54 = 4054, + CTRY_JAPAN55 = 4055, + CTRY_JAPAN56 = 4056, + CTRY_JAPAN57 = 4057, + CTRY_JAPAN58 = 4058, + CTRY_JAPAN59 = 4059, + CTRY_AUSTRALIA2 = 5000, + CTRY_CANADA2 = 5001, + CTRY_BELGIUM2 = 5002 +}; + +bool ath_is_world_regd(struct ath_regulatory *reg); +int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy, + int (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request)); +u32 ath_regd_get_band_ctl(struct ath_regulatory *reg, + enum ieee80211_band band); +int ath_reg_notifier_apply(struct wiphy *wiphy, + struct regulatory_request *request, + struct ath_regulatory *reg); + +#endif diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h new file mode 100644 index 00000000000..4d0e298cd1c --- /dev/null +++ b/drivers/net/wireless/ath/regd_common.h @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2008-2009 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 REGD_COMMON_H +#define REGD_COMMON_H + +enum EnumRd { + NO_ENUMRD = 0x00, + NULL1_WORLD = 0x03, + NULL1_ETSIB = 0x07, + NULL1_ETSIC = 0x08, + FCC1_FCCA = 0x10, + FCC1_WORLD = 0x11, + FCC4_FCCA = 0x12, + FCC5_FCCA = 0x13, + FCC6_FCCA = 0x14, + + FCC2_FCCA = 0x20, + FCC2_WORLD = 0x21, + FCC2_ETSIC = 0x22, + FCC6_WORLD = 0x23, + FRANCE_RES = 0x31, + FCC3_FCCA = 0x3A, + FCC3_WORLD = 0x3B, + + ETSI1_WORLD = 0x37, + ETSI3_ETSIA = 0x32, + ETSI2_WORLD = 0x35, + ETSI3_WORLD = 0x36, + ETSI4_WORLD = 0x30, + ETSI4_ETSIC = 0x38, + ETSI5_WORLD = 0x39, + ETSI6_WORLD = 0x34, + ETSI_RESERVED = 0x33, + + MKK1_MKKA = 0x40, + MKK1_MKKB = 0x41, + APL4_WORLD = 0x42, + MKK2_MKKA = 0x43, + APL_RESERVED = 0x44, + APL2_WORLD = 0x45, + APL2_APLC = 0x46, + APL3_WORLD = 0x47, + MKK1_FCCA = 0x48, + APL2_APLD = 0x49, + MKK1_MKKA1 = 0x4A, + MKK1_MKKA2 = 0x4B, + MKK1_MKKC = 0x4C, + + APL3_FCCA = 0x50, + APL1_WORLD = 0x52, + APL1_FCCA = 0x53, + APL1_APLA = 0x54, + APL1_ETSIC = 0x55, + APL2_ETSIC = 0x56, + APL5_WORLD = 0x58, + APL6_WORLD = 0x5B, + APL7_FCCA = 0x5C, + APL8_WORLD = 0x5D, + APL9_WORLD = 0x5E, + + WOR0_WORLD = 0x60, + WOR1_WORLD = 0x61, + WOR2_WORLD = 0x62, + WOR3_WORLD = 0x63, + WOR4_WORLD = 0x64, + WOR5_ETSIC = 0x65, + + WOR01_WORLD = 0x66, + WOR02_WORLD = 0x67, + EU1_WORLD = 0x68, + + WOR9_WORLD = 0x69, + WORA_WORLD = 0x6A, + WORB_WORLD = 0x6B, + + MKK3_MKKB = 0x80, + MKK3_MKKA2 = 0x81, + MKK3_MKKC = 0x82, + + MKK4_MKKB = 0x83, + MKK4_MKKA2 = 0x84, + MKK4_MKKC = 0x85, + + MKK5_MKKB = 0x86, + MKK5_MKKA2 = 0x87, + MKK5_MKKC = 0x88, + + MKK6_MKKB = 0x89, + MKK6_MKKA2 = 0x8A, + MKK6_MKKC = 0x8B, + + MKK7_MKKB = 0x8C, + MKK7_MKKA2 = 0x8D, + MKK7_MKKC = 0x8E, + + MKK8_MKKB = 0x8F, + MKK8_MKKA2 = 0x90, + MKK8_MKKC = 0x91, + + MKK14_MKKA1 = 0x92, + MKK15_MKKA1 = 0x93, + + MKK10_FCCA = 0xD0, + MKK10_MKKA1 = 0xD1, + MKK10_MKKC = 0xD2, + MKK10_MKKA2 = 0xD3, + + MKK11_MKKA = 0xD4, + MKK11_FCCA = 0xD5, + MKK11_MKKA1 = 0xD6, + MKK11_MKKC = 0xD7, + MKK11_MKKA2 = 0xD8, + + MKK12_MKKA = 0xD9, + MKK12_FCCA = 0xDA, + MKK12_MKKA1 = 0xDB, + MKK12_MKKC = 0xDC, + MKK12_MKKA2 = 0xDD, + + MKK13_MKKB = 0xDE, + + MKK3_MKKA = 0xF0, + MKK3_MKKA1 = 0xF1, + MKK3_FCCA = 0xF2, + MKK4_MKKA = 0xF3, + MKK4_MKKA1 = 0xF4, + MKK4_FCCA = 0xF5, + MKK9_MKKA = 0xF6, + MKK10_MKKA = 0xF7, + MKK6_MKKA1 = 0xF8, + MKK6_FCCA = 0xF9, + MKK7_MKKA1 = 0xFA, + MKK7_FCCA = 0xFB, + MKK9_FCCA = 0xFC, + MKK9_MKKA1 = 0xFD, + MKK9_MKKC = 0xFE, + MKK9_MKKA2 = 0xFF, + + WORLD = 0x0199, + DEBUG_REG_DMN = 0x01ff, +}; + +enum ctl_group { + CTL_FCC = 0x10, + CTL_MKK = 0x40, + CTL_ETSI = 0x30, +}; + +/* Regpair to CTL band mapping */ +static struct reg_dmn_pair_mapping regDomainPairs[] = { + /* regpair, 5 GHz CTL, 2 GHz CTL */ + {NO_ENUMRD, DEBUG_REG_DMN, DEBUG_REG_DMN}, + {NULL1_WORLD, NO_CTL, CTL_ETSI}, + {NULL1_ETSIB, NO_CTL, CTL_ETSI}, + {NULL1_ETSIC, NO_CTL, CTL_ETSI}, + + {FCC2_FCCA, CTL_FCC, CTL_FCC}, + {FCC2_WORLD, CTL_FCC, CTL_ETSI}, + {FCC2_ETSIC, CTL_FCC, CTL_ETSI}, + {FCC3_FCCA, CTL_FCC, CTL_FCC}, + {FCC3_WORLD, CTL_FCC, CTL_ETSI}, + {FCC4_FCCA, CTL_FCC, CTL_FCC}, + {FCC5_FCCA, CTL_FCC, CTL_FCC}, + {FCC6_FCCA, CTL_FCC, CTL_FCC}, + {FCC6_WORLD, CTL_FCC, CTL_ETSI}, + + {ETSI1_WORLD, CTL_ETSI, CTL_ETSI}, + {ETSI2_WORLD, CTL_ETSI, CTL_ETSI}, + {ETSI3_WORLD, CTL_ETSI, CTL_ETSI}, + {ETSI4_WORLD, CTL_ETSI, CTL_ETSI}, + {ETSI5_WORLD, CTL_ETSI, CTL_ETSI}, + {ETSI6_WORLD, CTL_ETSI, CTL_ETSI}, + + /* XXX: For ETSI3_ETSIA, Was NO_CTL meant for the 2 GHz band ? */ + {ETSI3_ETSIA, CTL_ETSI, CTL_ETSI}, + {FRANCE_RES, CTL_ETSI, CTL_ETSI}, + + {FCC1_WORLD, CTL_FCC, CTL_ETSI}, + {FCC1_FCCA, CTL_FCC, CTL_FCC}, + {APL1_WORLD, CTL_FCC, CTL_ETSI}, + {APL2_WORLD, CTL_FCC, CTL_ETSI}, + {APL3_WORLD, CTL_FCC, CTL_ETSI}, + {APL4_WORLD, CTL_FCC, CTL_ETSI}, + {APL5_WORLD, CTL_FCC, CTL_ETSI}, + {APL6_WORLD, CTL_ETSI, CTL_ETSI}, + {APL8_WORLD, CTL_ETSI, CTL_ETSI}, + {APL9_WORLD, CTL_ETSI, CTL_ETSI}, + + {APL3_FCCA, CTL_FCC, CTL_FCC}, + {APL1_ETSIC, CTL_FCC, CTL_ETSI}, + {APL2_ETSIC, CTL_FCC, CTL_ETSI}, + {APL2_APLD, CTL_FCC, NO_CTL}, + + {MKK1_MKKA, CTL_MKK, CTL_MKK}, + {MKK1_MKKB, CTL_MKK, CTL_MKK}, + {MKK1_FCCA, CTL_MKK, CTL_FCC}, + {MKK1_MKKA1, CTL_MKK, CTL_MKK}, + {MKK1_MKKA2, CTL_MKK, CTL_MKK}, + {MKK1_MKKC, CTL_MKK, CTL_MKK}, + + {MKK2_MKKA, CTL_MKK, CTL_MKK}, + {MKK3_MKKA, CTL_MKK, CTL_MKK}, + {MKK3_MKKB, CTL_MKK, CTL_MKK}, + {MKK3_MKKA1, CTL_MKK, CTL_MKK}, + {MKK3_MKKA2, CTL_MKK, CTL_MKK}, + {MKK3_MKKC, CTL_MKK, CTL_MKK}, + {MKK3_FCCA, CTL_MKK, CTL_FCC}, + + {MKK4_MKKA, CTL_MKK, CTL_MKK}, + {MKK4_MKKB, CTL_MKK, CTL_MKK}, + {MKK4_MKKA1, CTL_MKK, CTL_MKK}, + {MKK4_MKKA2, CTL_MKK, CTL_MKK}, + {MKK4_MKKC, CTL_MKK, CTL_MKK}, + {MKK4_FCCA, CTL_MKK, CTL_FCC}, + + {MKK5_MKKB, CTL_MKK, CTL_MKK}, + {MKK5_MKKA2, CTL_MKK, CTL_MKK}, + {MKK5_MKKC, CTL_MKK, CTL_MKK}, + + {MKK6_MKKB, CTL_MKK, CTL_MKK}, + {MKK6_MKKA1, CTL_MKK, CTL_MKK}, + {MKK6_MKKA2, CTL_MKK, CTL_MKK}, + {MKK6_MKKC, CTL_MKK, CTL_MKK}, + {MKK6_FCCA, CTL_MKK, CTL_FCC}, + + {MKK7_MKKB, CTL_MKK, CTL_MKK}, + {MKK7_MKKA1, CTL_MKK, CTL_MKK}, + {MKK7_MKKA2, CTL_MKK, CTL_MKK}, + {MKK7_MKKC, CTL_MKK, CTL_MKK}, + {MKK7_FCCA, CTL_MKK, CTL_FCC}, + + {MKK8_MKKB, CTL_MKK, CTL_MKK}, + {MKK8_MKKA2, CTL_MKK, CTL_MKK}, + {MKK8_MKKC, CTL_MKK, CTL_MKK}, + + {MKK9_MKKA, CTL_MKK, CTL_MKK}, + {MKK9_FCCA, CTL_MKK, CTL_FCC}, + {MKK9_MKKA1, CTL_MKK, CTL_MKK}, + {MKK9_MKKA2, CTL_MKK, CTL_MKK}, + {MKK9_MKKC, CTL_MKK, CTL_MKK}, + + {MKK10_MKKA, CTL_MKK, CTL_MKK}, + {MKK10_FCCA, CTL_MKK, CTL_FCC}, + {MKK10_MKKA1, CTL_MKK, CTL_MKK}, + {MKK10_MKKA2, CTL_MKK, CTL_MKK}, + {MKK10_MKKC, CTL_MKK, CTL_MKK}, + + {MKK11_MKKA, CTL_MKK, CTL_MKK}, + {MKK11_FCCA, CTL_MKK, CTL_FCC}, + {MKK11_MKKA1, CTL_MKK, CTL_MKK}, + {MKK11_MKKA2, CTL_MKK, CTL_MKK}, + {MKK11_MKKC, CTL_MKK, CTL_MKK}, + + {MKK12_MKKA, CTL_MKK, CTL_MKK}, + {MKK12_FCCA, CTL_MKK, CTL_FCC}, + {MKK12_MKKA1, CTL_MKK, CTL_MKK}, + {MKK12_MKKA2, CTL_MKK, CTL_MKK}, + {MKK12_MKKC, CTL_MKK, CTL_MKK}, + + {MKK13_MKKB, CTL_MKK, CTL_MKK}, + {MKK14_MKKA1, CTL_MKK, CTL_MKK}, + {MKK15_MKKA1, CTL_MKK, CTL_MKK}, + + {WOR0_WORLD, NO_CTL, NO_CTL}, + {WOR1_WORLD, NO_CTL, NO_CTL}, + {WOR2_WORLD, NO_CTL, NO_CTL}, + {WOR3_WORLD, NO_CTL, NO_CTL}, + {WOR4_WORLD, NO_CTL, NO_CTL}, + {WOR5_ETSIC, NO_CTL, NO_CTL}, + {WOR01_WORLD, NO_CTL, NO_CTL}, + {WOR02_WORLD, NO_CTL, NO_CTL}, + {EU1_WORLD, NO_CTL, NO_CTL}, + {WOR9_WORLD, NO_CTL, NO_CTL}, + {WORA_WORLD, NO_CTL, NO_CTL}, + {WORB_WORLD, NO_CTL, NO_CTL}, +}; + +static struct country_code_to_enum_rd allCountries[] = { + {CTRY_DEBUG, NO_ENUMRD, "DB"}, + {CTRY_DEFAULT, FCC1_FCCA, "CO"}, + {CTRY_ALBANIA, NULL1_WORLD, "AL"}, + {CTRY_ALGERIA, NULL1_WORLD, "DZ"}, + {CTRY_ARGENTINA, APL3_WORLD, "AR"}, + {CTRY_ARMENIA, ETSI4_WORLD, "AM"}, + {CTRY_AUSTRALIA, FCC2_WORLD, "AU"}, + {CTRY_AUSTRALIA2, FCC6_WORLD, "AU"}, + {CTRY_AUSTRIA, ETSI1_WORLD, "AT"}, + {CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ"}, + {CTRY_BAHRAIN, APL6_WORLD, "BH"}, + {CTRY_BELARUS, ETSI1_WORLD, "BY"}, + {CTRY_BELGIUM, ETSI1_WORLD, "BE"}, + {CTRY_BELGIUM2, ETSI4_WORLD, "BL"}, + {CTRY_BELIZE, APL1_ETSIC, "BZ"}, + {CTRY_BOLIVIA, APL1_ETSIC, "BO"}, + {CTRY_BOSNIA_HERZ, ETSI1_WORLD, "BA"}, + {CTRY_BRAZIL, FCC3_WORLD, "BR"}, + {CTRY_BRUNEI_DARUSSALAM, APL1_WORLD, "BN"}, + {CTRY_BULGARIA, ETSI6_WORLD, "BG"}, + {CTRY_CANADA, FCC2_FCCA, "CA"}, + {CTRY_CANADA2, FCC6_FCCA, "CA"}, + {CTRY_CHILE, APL6_WORLD, "CL"}, + {CTRY_CHINA, APL1_WORLD, "CN"}, + {CTRY_COLOMBIA, FCC1_FCCA, "CO"}, + {CTRY_COSTA_RICA, FCC1_WORLD, "CR"}, + {CTRY_CROATIA, ETSI3_WORLD, "HR"}, + {CTRY_CYPRUS, ETSI1_WORLD, "CY"}, + {CTRY_CZECH, ETSI3_WORLD, "CZ"}, + {CTRY_DENMARK, ETSI1_WORLD, "DK"}, + {CTRY_DOMINICAN_REPUBLIC, FCC1_FCCA, "DO"}, + {CTRY_ECUADOR, FCC1_WORLD, "EC"}, + {CTRY_EGYPT, ETSI3_WORLD, "EG"}, + {CTRY_EL_SALVADOR, FCC1_WORLD, "SV"}, + {CTRY_ESTONIA, ETSI1_WORLD, "EE"}, + {CTRY_FINLAND, ETSI1_WORLD, "FI"}, + {CTRY_FRANCE, ETSI1_WORLD, "FR"}, + {CTRY_GEORGIA, ETSI4_WORLD, "GE"}, + {CTRY_GERMANY, ETSI1_WORLD, "DE"}, + {CTRY_GREECE, ETSI1_WORLD, "GR"}, + {CTRY_GUATEMALA, FCC1_FCCA, "GT"}, + {CTRY_HONDURAS, NULL1_WORLD, "HN"}, + {CTRY_HONG_KONG, FCC2_WORLD, "HK"}, + {CTRY_HUNGARY, ETSI1_WORLD, "HU"}, + {CTRY_ICELAND, ETSI1_WORLD, "IS"}, + {CTRY_INDIA, APL6_WORLD, "IN"}, + {CTRY_INDONESIA, APL1_WORLD, "ID"}, + {CTRY_IRAN, APL1_WORLD, "IR"}, + {CTRY_IRELAND, ETSI1_WORLD, "IE"}, + {CTRY_ISRAEL, NULL1_WORLD, "IL"}, + {CTRY_ITALY, ETSI1_WORLD, "IT"}, + {CTRY_JAMAICA, ETSI1_WORLD, "JM"}, + + {CTRY_JAPAN, MKK1_MKKA, "JP"}, + {CTRY_JAPAN1, MKK1_MKKB, "JP"}, + {CTRY_JAPAN2, MKK1_FCCA, "JP"}, + {CTRY_JAPAN3, MKK2_MKKA, "JP"}, + {CTRY_JAPAN4, MKK1_MKKA1, "JP"}, + {CTRY_JAPAN5, MKK1_MKKA2, "JP"}, + {CTRY_JAPAN6, MKK1_MKKC, "JP"}, + {CTRY_JAPAN7, MKK3_MKKB, "JP"}, + {CTRY_JAPAN8, MKK3_MKKA2, "JP"}, + {CTRY_JAPAN9, MKK3_MKKC, "JP"}, + {CTRY_JAPAN10, MKK4_MKKB, "JP"}, + {CTRY_JAPAN11, MKK4_MKKA2, "JP"}, + {CTRY_JAPAN12, MKK4_MKKC, "JP"}, + {CTRY_JAPAN13, MKK5_MKKB, "JP"}, + {CTRY_JAPAN14, MKK5_MKKA2, "JP"}, + {CTRY_JAPAN15, MKK5_MKKC, "JP"}, + {CTRY_JAPAN16, MKK6_MKKB, "JP"}, + {CTRY_JAPAN17, MKK6_MKKA2, "JP"}, + {CTRY_JAPAN18, MKK6_MKKC, "JP"}, + {CTRY_JAPAN19, MKK7_MKKB, "JP"}, + {CTRY_JAPAN20, MKK7_MKKA2, "JP"}, + {CTRY_JAPAN21, MKK7_MKKC, "JP"}, + {CTRY_JAPAN22, MKK8_MKKB, "JP"}, + {CTRY_JAPAN23, MKK8_MKKA2, "JP"}, + {CTRY_JAPAN24, MKK8_MKKC, "JP"}, + {CTRY_JAPAN25, MKK3_MKKA, "JP"}, + {CTRY_JAPAN26, MKK3_MKKA1, "JP"}, + {CTRY_JAPAN27, MKK3_FCCA, "JP"}, + {CTRY_JAPAN28, MKK4_MKKA1, "JP"}, + {CTRY_JAPAN29, MKK4_FCCA, "JP"}, + {CTRY_JAPAN30, MKK6_MKKA1, "JP"}, + {CTRY_JAPAN31, MKK6_FCCA, "JP"}, + {CTRY_JAPAN32, MKK7_MKKA1, "JP"}, + {CTRY_JAPAN33, MKK7_FCCA, "JP"}, + {CTRY_JAPAN34, MKK9_MKKA, "JP"}, + {CTRY_JAPAN35, MKK10_MKKA, "JP"}, + {CTRY_JAPAN36, MKK4_MKKA, "JP"}, + {CTRY_JAPAN37, MKK9_FCCA, "JP"}, + {CTRY_JAPAN38, MKK9_MKKA1, "JP"}, + {CTRY_JAPAN39, MKK9_MKKC, "JP"}, + {CTRY_JAPAN40, MKK9_MKKA2, "JP"}, + {CTRY_JAPAN41, MKK10_FCCA, "JP"}, + {CTRY_JAPAN42, MKK10_MKKA1, "JP"}, + {CTRY_JAPAN43, MKK10_MKKC, "JP"}, + {CTRY_JAPAN44, MKK10_MKKA2, "JP"}, + {CTRY_JAPAN45, MKK11_MKKA, "JP"}, + {CTRY_JAPAN46, MKK11_FCCA, "JP"}, + {CTRY_JAPAN47, MKK11_MKKA1, "JP"}, + {CTRY_JAPAN48, MKK11_MKKC, "JP"}, + {CTRY_JAPAN49, MKK11_MKKA2, "JP"}, + {CTRY_JAPAN50, MKK12_MKKA, "JP"}, + {CTRY_JAPAN51, MKK12_FCCA, "JP"}, + {CTRY_JAPAN52, MKK12_MKKA1, "JP"}, + {CTRY_JAPAN53, MKK12_MKKC, "JP"}, + {CTRY_JAPAN54, MKK12_MKKA2, "JP"}, + {CTRY_JAPAN57, MKK13_MKKB, "JP"}, + {CTRY_JAPAN58, MKK14_MKKA1, "JP"}, + {CTRY_JAPAN59, MKK15_MKKA1, "JP"}, + + {CTRY_JORDAN, ETSI2_WORLD, "JO"}, + {CTRY_KAZAKHSTAN, NULL1_WORLD, "KZ"}, + {CTRY_KOREA_NORTH, APL9_WORLD, "KP"}, + {CTRY_KOREA_ROC, APL9_WORLD, "KR"}, + {CTRY_KOREA_ROC2, APL2_WORLD, "K2"}, + {CTRY_KOREA_ROC3, APL9_WORLD, "K3"}, + {CTRY_KUWAIT, NULL1_WORLD, "KW"}, + {CTRY_LATVIA, ETSI1_WORLD, "LV"}, + {CTRY_LEBANON, NULL1_WORLD, "LB"}, + {CTRY_LIECHTENSTEIN, ETSI1_WORLD, "LI"}, + {CTRY_LITHUANIA, ETSI1_WORLD, "LT"}, + {CTRY_LUXEMBOURG, ETSI1_WORLD, "LU"}, + {CTRY_MACAU, FCC2_WORLD, "MO"}, + {CTRY_MACEDONIA, NULL1_WORLD, "MK"}, + {CTRY_MALAYSIA, APL8_WORLD, "MY"}, + {CTRY_MALTA, ETSI1_WORLD, "MT"}, + {CTRY_MEXICO, FCC1_FCCA, "MX"}, + {CTRY_MONACO, ETSI4_WORLD, "MC"}, + {CTRY_MOROCCO, NULL1_WORLD, "MA"}, + {CTRY_NEPAL, APL1_WORLD, "NP"}, + {CTRY_NETHERLANDS, ETSI1_WORLD, "NL"}, + {CTRY_NETHERLANDS_ANTILLES, ETSI1_WORLD, "AN"}, + {CTRY_NEW_ZEALAND, FCC2_ETSIC, "NZ"}, + {CTRY_NORWAY, ETSI1_WORLD, "NO"}, + {CTRY_OMAN, APL6_WORLD, "OM"}, + {CTRY_PAKISTAN, NULL1_WORLD, "PK"}, + {CTRY_PANAMA, FCC1_FCCA, "PA"}, + {CTRY_PAPUA_NEW_GUINEA, FCC1_WORLD, "PG"}, + {CTRY_PERU, APL1_WORLD, "PE"}, + {CTRY_PHILIPPINES, APL1_WORLD, "PH"}, + {CTRY_POLAND, ETSI1_WORLD, "PL"}, + {CTRY_PORTUGAL, ETSI1_WORLD, "PT"}, + {CTRY_PUERTO_RICO, FCC1_FCCA, "PR"}, + {CTRY_QATAR, NULL1_WORLD, "QA"}, + {CTRY_ROMANIA, NULL1_WORLD, "RO"}, + {CTRY_RUSSIA, NULL1_WORLD, "RU"}, + {CTRY_SAUDI_ARABIA, NULL1_WORLD, "SA"}, + {CTRY_SERBIA_MONTENEGRO, ETSI1_WORLD, "CS"}, + {CTRY_SINGAPORE, APL6_WORLD, "SG"}, + {CTRY_SLOVAKIA, ETSI1_WORLD, "SK"}, + {CTRY_SLOVENIA, ETSI1_WORLD, "SI"}, + {CTRY_SOUTH_AFRICA, FCC3_WORLD, "ZA"}, + {CTRY_SPAIN, ETSI1_WORLD, "ES"}, + {CTRY_SRI_LANKA, FCC3_WORLD, "LK"}, + {CTRY_SWEDEN, ETSI1_WORLD, "SE"}, + {CTRY_SWITZERLAND, ETSI1_WORLD, "CH"}, + {CTRY_SYRIA, NULL1_WORLD, "SY"}, + {CTRY_TAIWAN, APL3_FCCA, "TW"}, + {CTRY_THAILAND, NULL1_WORLD, "TH"}, + {CTRY_TRINIDAD_Y_TOBAGO, ETSI4_WORLD, "TT"}, + {CTRY_TUNISIA, ETSI3_WORLD, "TN"}, + {CTRY_TURKEY, ETSI3_WORLD, "TR"}, + {CTRY_UKRAINE, NULL1_WORLD, "UA"}, + {CTRY_UAE, NULL1_WORLD, "AE"}, + {CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"}, + {CTRY_UNITED_STATES, FCC3_FCCA, "US"}, + /* This "PS" is for US public safety actually... to support this we + * would need to assign new special alpha2 to CRDA db as with the world + * regdomain and use another alpha2 */ + {CTRY_UNITED_STATES_FCC49, FCC4_FCCA, "PS"}, + {CTRY_URUGUAY, APL2_WORLD, "UY"}, + {CTRY_UZBEKISTAN, FCC3_FCCA, "UZ"}, + {CTRY_VENEZUELA, APL2_ETSIC, "VE"}, + {CTRY_VIET_NAM, NULL1_WORLD, "VN"}, + {CTRY_YEMEN, NULL1_WORLD, "YE"}, + {CTRY_ZIMBABWE, NULL1_WORLD, "ZW"}, +}; + +#endif |