summaryrefslogtreecommitdiff
path: root/package/mac80211/patches
diff options
context:
space:
mode:
Diffstat (limited to 'package/mac80211/patches')
-rw-r--r--package/mac80211/patches/020-nl80211_fix.patch12
-rw-r--r--package/mac80211/patches/300-ath5k_cc_lock_fix.patch10
-rw-r--r--package/mac80211/patches/300-mac80211_release_reorder_fix.patch25
-rw-r--r--package/mac80211/patches/301-ath9k_cc_lock_fix.patch10
-rw-r--r--package/mac80211/patches/403-ath9k-fix-invalid-mac-address-handling.patch2
-rw-r--r--package/mac80211/patches/406-ath9k-set-AH_USE_EEPROM-only-if-no-platform-data-present.patch6
-rw-r--r--package/mac80211/patches/407-ath9k-override-mac-address-from-platform-data.patch2
-rw-r--r--package/mac80211/patches/408-ath9k_tweak_rx_intr_mitigation.patch2
-rw-r--r--package/mac80211/patches/409-ath9k-add-wndr3700-antenna-initialization.patch8
-rw-r--r--package/mac80211/patches/520-ath9k_common_clockrate.patch116
-rw-r--r--package/mac80211/patches/520-ath9k_ps_survey_fix.patch (renamed from package/mac80211/patches/527-ath9k_ps_survey_fix.patch)0
-rw-r--r--package/mac80211/patches/521-ath5k_common_clockrate.patch65
-rw-r--r--package/mac80211/patches/521-ath9k_ani_listen_time_fix.patch (renamed from package/mac80211/patches/528-ath9k_ani_listen_time_fix.patch)0
-rw-r--r--package/mac80211/patches/522-ath9k_ani_overflow_fix.patch (renamed from package/mac80211/patches/529-ath9k_ani_overflow_fix.patch)0
-rw-r--r--package/mac80211/patches/522-ath_common_counters.patch347
-rw-r--r--package/mac80211/patches/523-ath5k_use_common_counters.patch104
-rw-r--r--package/mac80211/patches/523-ath9k_cycle_counter_lock_fix.patch (renamed from package/mac80211/patches/530-ath9k_cycle_counter_lock_fix.patch)0
-rw-r--r--package/mac80211/patches/524-mac80211_survey_channel_stats.patch105
-rw-r--r--package/mac80211/patches/525-ath9k_channel_count_check.patch33
-rw-r--r--package/mac80211/patches/526-ath9k_survey_channel_stats.patch183
-rw-r--r--package/mac80211/patches/540-ath9k_rc_debugfs.patch363
-rw-r--r--package/mac80211/patches/541-ath9k_rc_rate_table_cleanup.patch158
-rw-r--r--package/mac80211/patches/542-ath9k_no_mode_idx.patch247
-rw-r--r--package/mac80211/patches/550-ath9k_interrupt_mask_optimization.patch282
-rw-r--r--package/mac80211/patches/551-ath9k_isr_optimization.patch60
-rw-r--r--package/mac80211/patches/552-ath9k_txdesc_optimization.patch248
-rw-r--r--package/mac80211/patches/553-ath9k_no_node_rssi.patch145
-rw-r--r--package/mac80211/patches/554-ath9k_bt_timer_start.patch15
-rw-r--r--package/mac80211/patches/555-ath9k_hw_gettsf32_static.patch25
-rw-r--r--package/mac80211/patches/556-ath9k_desc_alignment.patch49
30 files changed, 1622 insertions, 1000 deletions
diff --git a/package/mac80211/patches/020-nl80211_fix.patch b/package/mac80211/patches/020-nl80211_fix.patch
deleted file mode 100644
index d7fb4df3e2..0000000000
--- a/package/mac80211/patches/020-nl80211_fix.patch
+++ /dev/null
@@ -1,12 +0,0 @@
---- a/compat/compat-2.6.37.c
-+++ b/compat/compat-2.6.37.c
-@@ -130,7 +130,8 @@ int compat_genl_register_family_with_ops
- __copy(dumpit);
- __copy(done);
- #undef __copy
-- ops[i].ops.doit = nl_doit_wrapper;
-+ if (ops[i].doit)
-+ ops[i].ops.doit = nl_doit_wrapper;
- ret = genl_register_ops(&family->family, &ops[i].ops);
- if (ret < 0)
- goto error_ops;
diff --git a/package/mac80211/patches/300-ath5k_cc_lock_fix.patch b/package/mac80211/patches/300-ath5k_cc_lock_fix.patch
new file mode 100644
index 0000000000..46f1202d09
--- /dev/null
+++ b/package/mac80211/patches/300-ath5k_cc_lock_fix.patch
@@ -0,0 +1,10 @@
+--- a/drivers/net/wireless/ath/ath5k/base.c
++++ b/drivers/net/wireless/ath/ath5k/base.c
+@@ -3605,6 +3605,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
+ common->ah = sc->ah;
+ common->hw = hw;
+ common->cachelsz = csz << 2; /* convert to bytes */
++ spin_lock_init(&common->cc_lock);
+
+ /* Initialize device */
+ ret = ath5k_hw_attach(sc);
diff --git a/package/mac80211/patches/300-mac80211_release_reorder_fix.patch b/package/mac80211/patches/300-mac80211_release_reorder_fix.patch
deleted file mode 100644
index a42d7ec13a..0000000000
--- a/package/mac80211/patches/300-mac80211_release_reorder_fix.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-[PATCH] mac80211: hoist sta->lock from reorder release timer
-
-The patch "mac80211: AMPDU rx reorder timeout timer" clashes
-with "mac80211: use netif_receive_skb in ieee80211_rx callpath"
-
-The timer itself is part of the station's private struct and
-it gets killed whenever the station is removed. Therefore
-the extra sta->lock protection (that can interferes with the
-tx path) is not necessary.
-
-Reported-by: Ming Lei <tom.leiming@gmail.com>
-Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
----
---- a/net/mac80211/agg-rx.c
-+++ b/net/mac80211/agg-rx.c
-@@ -129,9 +129,7 @@ static void sta_rx_agg_reorder_timer_exp
- timer_to_tid[0]);
-
- rcu_read_lock();
-- spin_lock(&sta->lock);
- ieee80211_release_reorder_timeout(sta, *ptid);
-- spin_unlock(&sta->lock);
- rcu_read_unlock();
- }
-
diff --git a/package/mac80211/patches/301-ath9k_cc_lock_fix.patch b/package/mac80211/patches/301-ath9k_cc_lock_fix.patch
new file mode 100644
index 0000000000..b9e82d18a4
--- /dev/null
+++ b/package/mac80211/patches/301-ath9k_cc_lock_fix.patch
@@ -0,0 +1,10 @@
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -577,6 +577,7 @@ static int ath9k_init_softc(u16 devid, s
+ common->hw = sc->hw;
+ common->priv = sc;
+ common->debug_mask = ath9k_debug;
++ spin_lock_init(&common->cc_lock);
+
+ spin_lock_init(&sc->wiphy_lock);
+ spin_lock_init(&sc->sc_resetlock);
diff --git a/package/mac80211/patches/403-ath9k-fix-invalid-mac-address-handling.patch b/package/mac80211/patches/403-ath9k-fix-invalid-mac-address-handling.patch
index 7dd3584d68..1f3712a609 100644
--- a/package/mac80211/patches/403-ath9k-fix-invalid-mac-address-handling.patch
+++ b/package/mac80211/patches/403-ath9k-fix-invalid-mac-address-handling.patch
@@ -8,7 +8,7 @@
#include <asm/unaligned.h>
#include "hw.h"
-@@ -446,8 +447,16 @@ static int ath9k_hw_init_macaddr(struct
+@@ -449,8 +450,16 @@ static int ath9k_hw_init_macaddr(struct
common->macaddr[2 * i] = eeval >> 8;
common->macaddr[2 * i + 1] = eeval & 0xff;
}
diff --git a/package/mac80211/patches/406-ath9k-set-AH_USE_EEPROM-only-if-no-platform-data-present.patch b/package/mac80211/patches/406-ath9k-set-AH_USE_EEPROM-only-if-no-platform-data-present.patch
index 1d90d6c14c..615bce5619 100644
--- a/package/mac80211/patches/406-ath9k-set-AH_USE_EEPROM-only-if-no-platform-data-present.patch
+++ b/package/mac80211/patches/406-ath9k-set-AH_USE_EEPROM-only-if-no-platform-data-present.patch
@@ -10,7 +10,7 @@
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -555,6 +555,7 @@ static int ath9k_init_softc(u16 devid, s
+@@ -559,6 +559,7 @@ static int ath9k_init_softc(u16 devid, s
{
struct ath_hw *ah = NULL;
struct ath_common *common;
@@ -18,7 +18,7 @@
int ret = 0, i;
int csz = 0;
-@@ -566,6 +567,10 @@ static int ath9k_init_softc(u16 devid, s
+@@ -570,6 +571,10 @@ static int ath9k_init_softc(u16 devid, s
ah->hw_version.subsysid = subsysid;
sc->sc_ah = ah;
@@ -31,7 +31,7 @@
common->bus_ops = bus_ops;
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -417,10 +417,6 @@ static void ath9k_hw_init_defaults(struc
+@@ -420,10 +420,6 @@ static void ath9k_hw_init_defaults(struc
ah->hw_version.magic = AR5416_MAGIC;
ah->hw_version.subvendorid = 0;
diff --git a/package/mac80211/patches/407-ath9k-override-mac-address-from-platform-data.patch b/package/mac80211/patches/407-ath9k-override-mac-address-from-platform-data.patch
index 1a5ff02bb3..315e7992c5 100644
--- a/package/mac80211/patches/407-ath9k-override-mac-address-from-platform-data.patch
+++ b/package/mac80211/patches/407-ath9k-override-mac-address-from-platform-data.patch
@@ -11,7 +11,7 @@
#include "hw.h"
#include "hw-ops.h"
#include "rc.h"
-@@ -431,18 +433,23 @@ static void ath9k_hw_init_defaults(struc
+@@ -434,18 +436,23 @@ static void ath9k_hw_init_defaults(struc
static int ath9k_hw_init_macaddr(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
diff --git a/package/mac80211/patches/408-ath9k_tweak_rx_intr_mitigation.patch b/package/mac80211/patches/408-ath9k_tweak_rx_intr_mitigation.patch
index c2bf8187ac..b9bf3e70d1 100644
--- a/package/mac80211/patches/408-ath9k_tweak_rx_intr_mitigation.patch
+++ b/package/mac80211/patches/408-ath9k_tweak_rx_intr_mitigation.patch
@@ -1,6 +1,6 @@
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -1414,7 +1414,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st
+@@ -1420,7 +1420,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st
if (ah->config.rx_intr_mitigation) {
REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500);
diff --git a/package/mac80211/patches/409-ath9k-add-wndr3700-antenna-initialization.patch b/package/mac80211/patches/409-ath9k-add-wndr3700-antenna-initialization.patch
index b79dd51a23..2007e4a6f7 100644
--- a/package/mac80211/patches/409-ath9k-add-wndr3700-antenna-initialization.patch
+++ b/package/mac80211/patches/409-ath9k-add-wndr3700-antenna-initialization.patch
@@ -8,7 +8,7 @@
#include "ath9k.h"
static char *dev_info = "ath9k";
-@@ -570,6 +571,8 @@ static int ath9k_init_softc(u16 devid, s
+@@ -574,6 +575,8 @@ static int ath9k_init_softc(u16 devid, s
pdata = (struct ath9k_platform_data *) sc->dev->platform_data;
if (!pdata)
ah->ah_flags |= AH_USE_EEPROM;
@@ -17,7 +17,7 @@
common = ath9k_hw_common(ah);
common->ops = &ath9k_common_ops;
-@@ -699,6 +702,24 @@ void ath9k_set_hw_capab(struct ath_softc
+@@ -704,6 +707,24 @@ void ath9k_set_hw_capab(struct ath_softc
SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
}
@@ -42,7 +42,7 @@
int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
const struct ath_bus_ops *bus_ops)
{
-@@ -717,6 +738,9 @@ int ath9k_init_device(u16 devid, struct
+@@ -722,6 +743,9 @@ int ath9k_init_device(u16 devid, struct
common = ath9k_hw_common(ah);
ath9k_set_hw_capab(sc, hw);
@@ -54,7 +54,7 @@
ath9k_reg_notifier);
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
-@@ -637,6 +637,8 @@ struct ath_softc {
+@@ -639,6 +639,8 @@ struct ath_softc {
int beacon_interval;
diff --git a/package/mac80211/patches/520-ath9k_common_clockrate.patch b/package/mac80211/patches/520-ath9k_common_clockrate.patch
deleted file mode 100644
index 3266e0e103..0000000000
--- a/package/mac80211/patches/520-ath9k_common_clockrate.patch
+++ /dev/null
@@ -1,116 +0,0 @@
---- a/drivers/net/wireless/ath/ath.h
-+++ b/drivers/net/wireless/ath/ath.h
-@@ -145,6 +145,8 @@ struct ath_common {
- DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX);
- enum ath_crypt_caps crypt_caps;
-
-+ unsigned int clockrate;
-+
- struct ath_regulatory regulatory;
- const struct ath_ops *ops;
- const struct ath_bus_ops *bus_ops;
---- a/drivers/net/wireless/ath/ath9k/hw.c
-+++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -91,29 +91,32 @@ static void ath9k_hw_ani_cache_ini_regs(
- /* Helper Functions */
- /********************/
-
--static u32 ath9k_hw_mac_clks(struct ath_hw *ah, u32 usecs)
-+static void ath9k_hw_set_clockrate(struct ath_hw *ah)
- {
- struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
-+ struct ath_common *common = ath9k_hw_common(ah);
-+ unsigned int clockrate;
-
- 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;
--
-- if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)
-- return usecs * ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
-+ clockrate = ATH9K_CLOCK_RATE_CCK;
-+ else if (conf->channel->band == IEEE80211_BAND_2GHZ)
-+ clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
-+ else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)
-+ clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
- else
-- return usecs * ATH9K_CLOCK_RATE_5GHZ_OFDM;
-+ clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM;
-+
-+ if (conf_is_ht40(conf))
-+ clockrate *= 2;
-+
-+ common->clockrate = clockrate;
- }
-
- static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs)
- {
-- struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
-+ struct ath_common *common = ath9k_hw_common(ah);
-
-- if (conf_is_ht40(conf))
-- return ath9k_hw_mac_clks(ah, usecs) * 2;
-- else
-- return ath9k_hw_mac_clks(ah, usecs);
-+ return usecs * common->clockrate;
- }
-
- bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout)
-@@ -1168,6 +1171,7 @@ static bool ath9k_hw_channel_change(stru
- "Failed to set channel\n");
- return false;
- }
-+ ath9k_hw_set_clockrate(ah);
-
- ah->eep_ops->set_txpower(ah, chan,
- ath9k_regd_get_ctl(regulatory, chan),
-@@ -1380,6 +1384,8 @@ int ath9k_hw_reset(struct ath_hw *ah, st
- if (r)
- return r;
-
-+ ath9k_hw_set_clockrate(ah);
-+
- ENABLE_REGWRITE_BUFFER(ah);
-
- for (i = 0; i < AR_NUM_DCU; i++)
---- a/drivers/net/wireless/ath/ath9k/ani.c
-+++ b/drivers/net/wireless/ath/ath9k/ani.c
-@@ -465,35 +465,13 @@ static void ath9k_hw_ani_lower_immunity(
- ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1);
- }
-
--static u8 ath9k_hw_chan_2_clockrate_mhz(struct ath_hw *ah)
--{
-- struct ath9k_channel *chan = ah->curchan;
-- struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
-- u8 clockrate; /* in MHz */
--
-- if (!ah->curchan) /* should really check for CCK instead */
-- clockrate = ATH9K_CLOCK_RATE_CCK;
-- else if (conf->channel->band == IEEE80211_BAND_2GHZ)
-- clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
-- else if (IS_CHAN_A_FAST_CLOCK(ah, chan))
-- clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
-- else
-- clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM;
--
-- if (conf_is_ht40(conf))
-- return clockrate * 2;
--
-- return clockrate;
--}
--
- static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah)
- {
-+ struct ath_common *common = ath9k_hw_common(ah);
- int32_t listen_time;
-- int32_t clock_rate;
-
- ath9k_hw_update_cycle_counters(ah);
-- clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000;
-- listen_time = ah->listen_time / clock_rate;
-+ listen_time = ah->listen_time / (common->clockrate * 1000);
- ah->listen_time = 0;
-
- return listen_time;
diff --git a/package/mac80211/patches/527-ath9k_ps_survey_fix.patch b/package/mac80211/patches/520-ath9k_ps_survey_fix.patch
index f130f4132e..f130f4132e 100644
--- a/package/mac80211/patches/527-ath9k_ps_survey_fix.patch
+++ b/package/mac80211/patches/520-ath9k_ps_survey_fix.patch
diff --git a/package/mac80211/patches/521-ath5k_common_clockrate.patch b/package/mac80211/patches/521-ath5k_common_clockrate.patch
deleted file mode 100644
index 08c18342e7..0000000000
--- a/package/mac80211/patches/521-ath5k_common_clockrate.patch
+++ /dev/null
@@ -1,65 +0,0 @@
---- a/drivers/net/wireless/ath/ath5k/pcu.c
-+++ b/drivers/net/wireless/ath/ath5k/pcu.c
-@@ -207,7 +207,8 @@ static int ath5k_hw_set_cts_timeout(stru
- */
- unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec)
- {
-- return usec * ath5k_hw_get_clockrate(ah);
-+ struct ath_common *common = ath5k_hw_common(ah);
-+ return usec * common->clockrate;
- }
-
- /**
-@@ -216,17 +217,19 @@ unsigned int ath5k_hw_htoclock(struct at
- */
- unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock)
- {
-- return clock / ath5k_hw_get_clockrate(ah);
-+ struct ath_common *common = ath5k_hw_common(ah);
-+ return clock / common->clockrate;
- }
-
- /**
-- * ath5k_hw_get_clockrate - Get the clock rate for current mode
-+ * ath5k_hw_set_clockrate - Set common->clockrate for the current channel
- *
- * @ah: The &struct ath5k_hw
- */
--unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah)
-+void ath5k_hw_set_clockrate(struct ath5k_hw *ah)
- {
- struct ieee80211_channel *channel = ah->ah_current_channel;
-+ struct ath_common *common = ath5k_hw_common(ah);
- int clock;
-
- if (channel->hw_value & CHANNEL_5GHZ)
-@@ -240,7 +243,7 @@ unsigned int ath5k_hw_get_clockrate(stru
- if (channel->hw_value & CHANNEL_TURBO)
- clock *= 2;
-
-- return clock;
-+ common->clockrate = clock;
- }
-
- /**
---- a/drivers/net/wireless/ath/ath5k/ath5k.h
-+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
-@@ -1201,7 +1201,7 @@ void ath5k_hw_set_ack_bitrate_high(struc
- /* Clock rate related functions */
- unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec);
- unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock);
--unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah);
-+void ath5k_hw_set_clockrate(struct ath5k_hw *ah);
-
- /* Queue Control Unit, DFS Control Unit Functions */
- int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
---- a/drivers/net/wireless/ath/ath5k/phy.c
-+++ b/drivers/net/wireless/ath/ath5k/phy.c
-@@ -1093,6 +1093,7 @@ int ath5k_hw_channel(struct ath5k_hw *ah
-
- ah->ah_current_channel = channel;
- ah->ah_turbo = channel->hw_value == CHANNEL_T ? true : false;
-+ ath5k_hw_set_clockrate(ah);
-
- return 0;
- }
diff --git a/package/mac80211/patches/528-ath9k_ani_listen_time_fix.patch b/package/mac80211/patches/521-ath9k_ani_listen_time_fix.patch
index 598286ec9e..598286ec9e 100644
--- a/package/mac80211/patches/528-ath9k_ani_listen_time_fix.patch
+++ b/package/mac80211/patches/521-ath9k_ani_listen_time_fix.patch
diff --git a/package/mac80211/patches/529-ath9k_ani_overflow_fix.patch b/package/mac80211/patches/522-ath9k_ani_overflow_fix.patch
index 45bb95d4ee..45bb95d4ee 100644
--- a/package/mac80211/patches/529-ath9k_ani_overflow_fix.patch
+++ b/package/mac80211/patches/522-ath9k_ani_overflow_fix.patch
diff --git a/package/mac80211/patches/522-ath_common_counters.patch b/package/mac80211/patches/522-ath_common_counters.patch
deleted file mode 100644
index f719575264..0000000000
--- a/package/mac80211/patches/522-ath_common_counters.patch
+++ /dev/null
@@ -1,347 +0,0 @@
---- a/drivers/net/wireless/ath/ath.h
-+++ b/drivers/net/wireless/ath/ath.h
-@@ -19,6 +19,7 @@
-
- #include <linux/skbuff.h>
- #include <linux/if_ether.h>
-+#include <linux/spinlock.h>
- #include <net/mac80211.h>
-
- /*
-@@ -42,6 +43,13 @@ struct ath_ani {
- struct timer_list timer;
- };
-
-+struct ath_cycle_counters {
-+ u32 cycles;
-+ u32 rx_busy;
-+ u32 rx_frame;
-+ u32 tx_frame;
-+};
-+
- enum ath_device_state {
- ATH_HW_UNAVAILABLE,
- ATH_HW_INITIALIZED,
-@@ -147,6 +155,10 @@ struct ath_common {
-
- unsigned int clockrate;
-
-+ spinlock_t cc_lock;
-+ struct ath_cycle_counters cc_ani;
-+ struct ath_cycle_counters cc_survey;
-+
- struct ath_regulatory regulatory;
- const struct ath_ops *ops;
- const struct ath_bus_ops *bus_ops;
-@@ -163,5 +175,7 @@ int ath_key_config(struct ath_common *co
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key);
- bool ath_hw_keyreset(struct ath_common *common, u16 entry);
-+void ath_hw_cycle_counters_update(struct ath_common *common);
-+int32_t ath_hw_get_listen_time(struct ath_common *common);
-
- #endif /* ATH_H */
---- a/drivers/net/wireless/ath/ath9k/ani.c
-+++ b/drivers/net/wireless/ath/ath9k/ani.c
-@@ -465,18 +465,6 @@ static void ath9k_hw_ani_lower_immunity(
- ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1);
- }
-
--static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah)
--{
-- struct ath_common *common = ath9k_hw_common(ah);
-- int32_t listen_time;
--
-- ath9k_hw_update_cycle_counters(ah);
-- listen_time = ah->listen_time / (common->clockrate * 1000);
-- ah->listen_time = 0;
--
-- return listen_time;
--}
--
- static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning)
- {
- struct ar5416AniState *aniState;
-@@ -655,7 +643,9 @@ static void ath9k_hw_ani_read_counters(s
- u32 phyCnt1, phyCnt2;
- int32_t listenTime;
-
-- listenTime = ath9k_hw_ani_get_listen_time(ah);
-+ ath_hw_cycle_counters_update(common);
-+ listenTime = ath_hw_get_listen_time(common);
-+
- if (listenTime < 0) {
- ah->stats.ast_ani_lneg++;
- ath9k_ani_restart(ah);
-@@ -796,54 +786,6 @@ void ath9k_hw_disable_mib_counters(struc
- }
- EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
-
--void ath9k_hw_update_cycle_counters(struct ath_hw *ah)
--{
-- struct ath_cycle_counters cc;
-- bool clear;
--
-- memcpy(&cc, &ah->cc, sizeof(cc));
--
-- /* freeze counters */
-- REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
--
-- ah->cc.cycles = REG_READ(ah, AR_CCCNT);
-- if (ah->cc.cycles < cc.cycles) {
-- clear = true;
-- goto skip;
-- }
--
-- ah->cc.rx_clear = REG_READ(ah, AR_RCCNT);
-- ah->cc.rx_frame = REG_READ(ah, AR_RFCNT);
-- ah->cc.tx_frame = REG_READ(ah, AR_TFCNT);
--
-- /* prevent wraparound */
-- if (ah->cc.cycles & BIT(31))
-- clear = true;
--
--#define CC_DELTA(_field, _reg) ah->cc_delta._field += ah->cc._field - cc._field
-- CC_DELTA(cycles, AR_CCCNT);
-- CC_DELTA(rx_frame, AR_RFCNT);
-- CC_DELTA(rx_clear, AR_RCCNT);
-- CC_DELTA(tx_frame, AR_TFCNT);
--#undef CC_DELTA
--
-- ah->listen_time += (ah->cc.cycles - cc.cycles) -
-- ((ah->cc.rx_frame - cc.rx_frame) +
-- (ah->cc.tx_frame - cc.tx_frame));
--
--skip:
-- if (clear) {
-- REG_WRITE(ah, AR_CCCNT, 0);
-- REG_WRITE(ah, AR_RFCNT, 0);
-- REG_WRITE(ah, AR_RCCNT, 0);
-- REG_WRITE(ah, AR_TFCNT, 0);
-- memset(&ah->cc, 0, sizeof(ah->cc));
-- }
--
-- /* unfreeze counters */
-- REG_WRITE(ah, AR_MIBC, 0);
--}
--
- /*
- * Process a MIB interrupt. We may potentially be invoked because
- * any of the MIB counters overflow/trigger so don't assume we're
---- a/drivers/net/wireless/ath/ath9k/ani.h
-+++ b/drivers/net/wireless/ath/ath9k/ani.h
-@@ -93,13 +93,6 @@ struct ath9k_mib_stats {
- u32 beacons;
- };
-
--struct ath_cycle_counters {
-- u32 cycles;
-- u32 rx_frame;
-- u32 rx_clear;
-- u32 tx_frame;
--};
--
- /* INI default values for ANI registers */
- struct ath9k_ani_default {
- u16 m1ThreshLow;
-@@ -164,7 +157,6 @@ struct ar5416Stats {
-
- void ath9k_enable_mib_counters(struct ath_hw *ah);
- void ath9k_hw_disable_mib_counters(struct ath_hw *ah);
--void ath9k_hw_update_cycle_counters(struct ath_hw *ah);
- void ath9k_hw_ani_setup(struct ath_hw *ah);
- void ath9k_hw_ani_init(struct ath_hw *ah);
- int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah,
---- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
-+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
-@@ -1254,13 +1254,12 @@ void ar9003_hw_bb_watchdog_dbg_info(stru
- "** BB mode: BB_gen_controls=0x%08x **\n",
- REG_READ(ah, AR_PHY_GEN_CTRL));
-
-- ath9k_hw_update_cycle_counters(ah);
--#define PCT(_field) (ah->cc_delta._field * 100 / ah->cc_delta.cycles)
-- if (ah->cc_delta.cycles)
-+#define PCT(_field) (common->cc_survey._field * 100 / common->cc_survey.cycles)
-+ if (common->cc_survey.cycles)
- ath_print(common, ATH_DBG_RESET,
- "** BB busy times: rx_clear=%d%%, "
- "rx_frame=%d%%, tx_frame=%d%% **\n",
-- PCT(rx_clear), PCT(rx_frame), PCT(tx_frame));
-+ PCT(rx_busy), PCT(rx_frame), PCT(tx_frame));
-
- ath_print(common, ATH_DBG_RESET,
- "==== BB update: done ====\n\n");
---- a/drivers/net/wireless/ath/ath9k/hw.h
-+++ b/drivers/net/wireless/ath/ath9k/hw.h
-@@ -740,8 +740,6 @@ struct ath_hw {
- int coarse_low[5];
- int firpwr[5];
- enum ath9k_ani_cmd ani_function;
-- struct ath_cycle_counters cc, cc_delta;
-- int32_t listen_time;
-
- /* Bluetooth coexistance */
- struct ath_btcoex_hw btcoex_hw;
---- a/drivers/net/wireless/ath/ath9k/main.c
-+++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -400,6 +400,7 @@ void ath_ani_calibrate(unsigned long dat
- bool aniflag = false;
- unsigned int timestamp = jiffies_to_msecs(jiffies);
- u32 cal_interval, short_cal_interval, long_cal_interval;
-+ unsigned long flags;
-
- if (ah->caldata && ah->caldata->nfcal_interference)
- long_cal_interval = ATH_LONG_CALINTERVAL_INT;
-@@ -450,8 +451,11 @@ void ath_ani_calibrate(unsigned long dat
- /* Skip all processing if there's nothing to do. */
- if (longcal || shortcal || aniflag) {
- /* Call ANI routine if necessary */
-- if (aniflag)
-+ if (aniflag) {
-+ spin_lock_irqsave(&common->cc_lock, flags);
- ath9k_hw_ani_monitor(ah, ah->curchan);
-+ spin_unlock_irqrestore(&common->cc_lock, flags);
-+ }
-
- /* Perform calibration if necessary */
- if (longcal || shortcal) {
-@@ -636,6 +640,7 @@ irqreturn_t ath_isr(int irq, void *dev)
-
- struct ath_softc *sc = dev;
- struct ath_hw *ah = sc->sc_ah;
-+ struct ath_common *common = ath9k_hw_common(ah);
- enum ath9k_int status;
- bool sched = false;
-
-@@ -685,7 +690,12 @@ irqreturn_t ath_isr(int irq, void *dev)
-
- if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
- (status & ATH9K_INT_BB_WATCHDOG)) {
-+
-+ spin_lock(&common->cc_lock);
-+ ath_hw_cycle_counters_update(common);
- ar9003_hw_bb_watchdog_dbg_info(ah);
-+ spin_unlock(&common->cc_lock);
-+
- goto chip_reset;
- }
-
---- a/drivers/net/wireless/ath/ath9k/reg.h
-+++ b/drivers/net/wireless/ath/ath9k/reg.h
-@@ -107,12 +107,6 @@
- #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
-
-@@ -1524,11 +1518,6 @@ enum {
- #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
---- a/drivers/net/wireless/ath/hw.c
-+++ b/drivers/net/wireless/ath/hw.c
-@@ -124,3 +124,62 @@ void ath_hw_setbssidmask(struct ath_comm
- REG_WRITE(ah, get_unaligned_le16(common->bssidmask + 4), AR_BSSMSKU);
- }
- EXPORT_SYMBOL(ath_hw_setbssidmask);
-+
-+
-+/**
-+ * ath_hw_cycle_counters_update - common function to update cycle counters
-+ *
-+ * @common: the ath_common struct for the device.
-+ *
-+ * This function is used to update all cycle counters in one place.
-+ * It has to be called while holding common->cc_lock!
-+ */
-+void ath_hw_cycle_counters_update(struct ath_common *common)
-+{
-+ u32 cycles, busy, rx, tx;
-+ void *ah = common->ah;
-+
-+ /* freeze */
-+ REG_WRITE(ah, AR_MIBC_FMC, AR_MIBC);
-+
-+ /* read */
-+ cycles = REG_READ(ah, AR_CCCNT);
-+ busy = REG_READ(ah, AR_RCCNT);
-+ rx = REG_READ(ah, AR_RFCNT);
-+ tx = REG_READ(ah, AR_TFCNT);
-+
-+ /* clear */
-+ REG_WRITE(ah, 0, AR_CCCNT);
-+ REG_WRITE(ah, 0, AR_RFCNT);
-+ REG_WRITE(ah, 0, AR_RCCNT);
-+ REG_WRITE(ah, 0, AR_TFCNT);
-+
-+ /* unfreeze */
-+ REG_WRITE(ah, 0, AR_MIBC);
-+
-+ /* update all cycle counters here */
-+ common->cc_ani.cycles += cycles;
-+ common->cc_ani.rx_busy += busy;
-+ common->cc_ani.rx_frame += rx;
-+ common->cc_ani.tx_frame += tx;
-+
-+ common->cc_survey.cycles += cycles;
-+ common->cc_survey.rx_busy += busy;
-+ common->cc_survey.rx_frame += rx;
-+ common->cc_survey.tx_frame += tx;
-+}
-+EXPORT_SYMBOL(ath_hw_cycle_counters_update);
-+
-+int32_t ath_hw_get_listen_time(struct ath_common *common)
-+{
-+ struct ath_cycle_counters *cc = &common->cc_ani;
-+ int32_t listen_time;
-+
-+ listen_time = (cc->cycles - cc->rx_frame - cc->tx_frame) /
-+ (common->clockrate * 1000);
-+
-+ memset(cc, 0, sizeof(*cc));
-+
-+ return listen_time;
-+}
-+EXPORT_SYMBOL(ath_hw_get_listen_time);
---- a/drivers/net/wireless/ath/reg.h
-+++ b/drivers/net/wireless/ath/reg.h
-@@ -17,6 +17,12 @@
- #ifndef ATH_REGISTERS_H
- #define ATH_REGISTERS_H
-
-+#define AR_MIBC 0x0040
-+#define AR_MIBC_COW 0x00000001
-+#define AR_MIBC_FMC 0x00000002
-+#define AR_MIBC_CMC 0x00000004
-+#define AR_MIBC_MCS 0x00000008
-+
- /*
- * BSSID mask registers. See ath_hw_set_bssid_mask()
- * for detailed documentation about these registers.
-@@ -24,6 +30,11 @@
- #define AR_BSSMSKL 0x80e0
- #define AR_BSSMSKU 0x80e4
-
-+#define AR_TFCNT 0x80ec
-+#define AR_RFCNT 0x80f0
-+#define AR_RCCNT 0x80f4
-+#define AR_CCCNT 0x80f8
-+
- #define AR_KEYTABLE_0 0x8800
- #define AR_KEYTABLE(_n) (AR_KEYTABLE_0 + ((_n)*32))
- #define AR_KEY_CACHE_SIZE 128
diff --git a/package/mac80211/patches/523-ath5k_use_common_counters.patch b/package/mac80211/patches/523-ath5k_use_common_counters.patch
deleted file mode 100644
index 1899f8d465..0000000000
--- a/package/mac80211/patches/523-ath5k_use_common_counters.patch
+++ /dev/null
@@ -1,104 +0,0 @@
---- a/drivers/net/wireless/ath/ath5k/ani.c
-+++ b/drivers/net/wireless/ath/ath5k/ani.c
-@@ -355,41 +355,28 @@ ath5k_ani_lower_immunity(struct ath5k_hw
-
-
- /**
-- * ath5k_hw_ani_get_listen_time() - Calculate time spent listening
-+ * ath5k_hw_ani_get_listen_time() - Update counters and return listening time
- *
- * Return an approximation of the time spent "listening" in milliseconds (ms)
-- * since the last call of this function by deducting the cycles spent
-- * transmitting and receiving from the total cycle count.
-- * Save profile count values for debugging/statistics and because we might want
-- * to use them later.
-- *
-- * We assume no one else clears these registers!
-+ * since the last call of this function.
-+ * Save a snapshot of the counter values for debugging/statistics.
- */
- static int
- ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as)
- {
-+ struct ath_common *common = ath5k_hw_common(ah);
- int listen;
-
-- /* freeze */
-- ath5k_hw_reg_write(ah, AR5K_MIBC_FMC, AR5K_MIBC);
-- /* read */
-- as->pfc_cycles = ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE);
-- as->pfc_busy = ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR);
-- as->pfc_tx = ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX);
-- as->pfc_rx = ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX);
-- /* clear */
-- 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);
-- /* un-freeze */
-- ath5k_hw_reg_write(ah, 0, AR5K_MIBC);
-+ spin_lock_bh(&common->cc_lock);
-
-- /* TODO: where does 44000 come from? (11g clock rate?) */
-- listen = (as->pfc_cycles - as->pfc_rx - as->pfc_tx) / 44000;
-+ ath_hw_cycle_counters_update(common);
-+ memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc));
-+
-+ /* clears common->cc_ani */
-+ listen = ath_hw_get_listen_time(common);
-+
-+ spin_unlock_bh(&common->cc_lock);
-
-- if (as->pfc_cycles == 0 || listen < 0)
-- return 0;
- return listen;
- }
-
---- a/drivers/net/wireless/ath/ath5k/ani.h
-+++ b/drivers/net/wireless/ath/ath5k/ani.h
-@@ -75,10 +75,7 @@ struct ath5k_ani_state {
- unsigned int cck_errors;
-
- /* debug/statistics only: numbers from last ANI calibration */
-- unsigned int pfc_tx;
-- unsigned int pfc_rx;
-- unsigned int pfc_busy;
-- unsigned int pfc_cycles;
-+ struct ath_cycle_counters last_cc;
- unsigned int last_listen;
- unsigned int last_ofdm_errors;
- unsigned int last_cck_errors;
---- a/drivers/net/wireless/ath/ath5k/debug.c
-+++ b/drivers/net/wireless/ath/ath5k/debug.c
-@@ -715,20 +715,21 @@ static ssize_t read_file_ani(struct file
- len += snprintf(buf+len, sizeof(buf)-len,
- "beacon RSSI average:\t%d\n",
- sc->ah->ah_beacon_rssi_avg.avg);
-+
-+#define CC_PRINT(_struct, _field) \
-+ _struct._field, \
-+ _struct.cycles > 0 ? \
-+ _struct._field*100/_struct.cycles : 0
-+
- len += snprintf(buf+len, sizeof(buf)-len, "profcnt tx\t\t%u\t(%d%%)\n",
-- as->pfc_tx,
-- as->pfc_cycles > 0 ?
-- as->pfc_tx*100/as->pfc_cycles : 0);
-+ CC_PRINT(as->last_cc, tx_frame));
- len += snprintf(buf+len, sizeof(buf)-len, "profcnt rx\t\t%u\t(%d%%)\n",
-- as->pfc_rx,
-- as->pfc_cycles > 0 ?
-- as->pfc_rx*100/as->pfc_cycles : 0);
-+ CC_PRINT(as->last_cc, rx_frame));
- len += snprintf(buf+len, sizeof(buf)-len, "profcnt busy\t\t%u\t(%d%%)\n",
-- as->pfc_busy,
-- as->pfc_cycles > 0 ?
-- as->pfc_busy*100/as->pfc_cycles : 0);
-+ CC_PRINT(as->last_cc, rx_busy));
-+#undef CC_PRINT
- len += snprintf(buf+len, sizeof(buf)-len, "profcnt cycles\t\t%u\n",
-- as->pfc_cycles);
-+ as->last_cc.cycles);
- len += snprintf(buf+len, sizeof(buf)-len,
- "listen time\t\t%d\tlast: %d\n",
- as->listen_time, as->last_listen);
diff --git a/package/mac80211/patches/530-ath9k_cycle_counter_lock_fix.patch b/package/mac80211/patches/523-ath9k_cycle_counter_lock_fix.patch
index 9f284df2cc..9f284df2cc 100644
--- a/package/mac80211/patches/530-ath9k_cycle_counter_lock_fix.patch
+++ b/package/mac80211/patches/523-ath9k_cycle_counter_lock_fix.patch
diff --git a/package/mac80211/patches/524-mac80211_survey_channel_stats.patch b/package/mac80211/patches/524-mac80211_survey_channel_stats.patch
deleted file mode 100644
index af71234121..0000000000
--- a/package/mac80211/patches/524-mac80211_survey_channel_stats.patch
+++ /dev/null
@@ -1,105 +0,0 @@
---- a/include/linux/nl80211.h
-+++ b/include/linux/nl80211.h
-@@ -1413,6 +1413,16 @@ enum nl80211_reg_rule_flags {
- * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
- * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
- * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used
-+ * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio
-+ * spent on this channel
-+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary
-+ * channel was sensed busy (either due to activity or energy detect)
-+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension
-+ * channel was sensed busy
-+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent
-+ * receiving data
-+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent
-+ * transmitting data
- * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
- * currently defined
- * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
-@@ -1422,6 +1432,11 @@ enum nl80211_survey_info {
- NL80211_SURVEY_INFO_FREQUENCY,
- NL80211_SURVEY_INFO_NOISE,
- NL80211_SURVEY_INFO_IN_USE,
-+ NL80211_SURVEY_INFO_CHANNEL_TIME,
-+ NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
-+ NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
-+ NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
-+ NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
-
- /* keep last */
- __NL80211_SURVEY_INFO_AFTER_LAST,
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -295,6 +295,11 @@ struct key_params {
- *
- * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
- * @SURVEY_INFO_IN_USE: channel is currently being used
-+ * @SURVEY_INFO_CHANNEL_TIME: channel active time (in ms) was filled in
-+ * @SURVEY_INFO_CHANNEL_TIME_BUSY: channel busy time was filled in
-+ * @SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: extension channel busy time was filled in
-+ * @SURVEY_INFO_CHANNEL_TIME_RX: channel receive time was filled in
-+ * @SURVEY_INFO_CHANNEL_TIME_TX: channel transmit time was filled in
- *
- * Used by the driver to indicate which info in &struct survey_info
- * it has filled in during the get_survey().
-@@ -302,6 +307,11 @@ struct key_params {
- enum survey_info_flags {
- SURVEY_INFO_NOISE_DBM = 1<<0,
- SURVEY_INFO_IN_USE = 1<<1,
-+ SURVEY_INFO_CHANNEL_TIME = 1<<2,
-+ SURVEY_INFO_CHANNEL_TIME_BUSY = 1<<3,
-+ SURVEY_INFO_CHANNEL_TIME_EXT_BUSY = 1<<4,
-+ SURVEY_INFO_CHANNEL_TIME_RX = 1<<5,
-+ SURVEY_INFO_CHANNEL_TIME_TX = 1<<6,
- };
-
- /**
-@@ -311,6 +321,11 @@ enum survey_info_flags {
- * @filled: bitflag of flags from &enum survey_info_flags
- * @noise: channel noise in dBm. This and all following fields are
- * optional
-+ * @channel_time: amount of time in ms the radio spent on the channel
-+ * @channel_time_busy: amount of time the primary channel was sensed busy
-+ * @channel_time_ext_busy: amount of time the extension channel was sensed busy
-+ * @channel_time_rx: amount of time the radio spent receiving data
-+ * @channel_time_tx: amount of time the radio spent transmitting data
- *
- * Used by dump_survey() to report back per-channel survey information.
- *
-@@ -319,6 +334,11 @@ enum survey_info_flags {
- */
- struct survey_info {
- struct ieee80211_channel *channel;
-+ u64 channel_time;
-+ u64 channel_time_busy;
-+ u64 channel_time_ext_busy;
-+ u64 channel_time_rx;
-+ u64 channel_time_tx;
- u32 filled;
- s8 noise;
- };
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -3153,6 +3153,21 @@ static int nl80211_send_survey(struct sk
- survey->noise);
- if (survey->filled & SURVEY_INFO_IN_USE)
- NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE);
-+ if (survey->filled & SURVEY_INFO_CHANNEL_TIME)
-+ NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
-+ survey->channel_time);
-+ if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
-+ NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
-+ survey->channel_time_busy);
-+ if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
-+ NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
-+ survey->channel_time_ext_busy);
-+ if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX)
-+ NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
-+ survey->channel_time_rx);
-+ if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX)
-+ NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
-+ survey->channel_time_tx);
-
- nla_nest_end(msg, infoattr);
-
diff --git a/package/mac80211/patches/525-ath9k_channel_count_check.patch b/package/mac80211/patches/525-ath9k_channel_count_check.patch
deleted file mode 100644
index ae743934bd..0000000000
--- a/package/mac80211/patches/525-ath9k_channel_count_check.patch
+++ /dev/null
@@ -1,33 +0,0 @@
---- a/drivers/net/wireless/ath/ath9k/hw.h
-+++ b/drivers/net/wireless/ath/ath9k/hw.h
-@@ -61,6 +61,8 @@
-
- #define ATH9K_RSSI_BAD -128
-
-+#define ATH9K_NUM_CHANNELS 38
-+
- /* Register read/write primitives */
- #define REG_WRITE(_ah, _reg, _val) \
- ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg))
-@@ -618,7 +620,7 @@ struct ath_hw {
- struct ath9k_hw_version hw_version;
- struct ath9k_ops_config config;
- struct ath9k_hw_capabilities caps;
-- struct ath9k_channel channels[38];
-+ struct ath9k_channel channels[ATH9K_NUM_CHANNELS];
- struct ath9k_channel *curchan;
-
- union {
---- a/drivers/net/wireless/ath/ath9k/init.c
-+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -482,6 +482,10 @@ static int ath9k_init_channels_rates(str
- {
- void *channels;
-
-+ BUILD_BUG_ON(ARRAY_SIZE(ath9k_2ghz_chantable) +
-+ ARRAY_SIZE(ath9k_5ghz_chantable) !=
-+ ATH9K_NUM_CHANNELS);
-+
- if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) {
- channels = kmemdup(ath9k_2ghz_chantable,
- sizeof(ath9k_2ghz_chantable), GFP_KERNEL);
diff --git a/package/mac80211/patches/526-ath9k_survey_channel_stats.patch b/package/mac80211/patches/526-ath9k_survey_channel_stats.patch
deleted file mode 100644
index 57ba398b02..0000000000
--- a/package/mac80211/patches/526-ath9k_survey_channel_stats.patch
+++ /dev/null
@@ -1,183 +0,0 @@
---- a/drivers/net/wireless/ath/ath9k/ath9k.h
-+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
-@@ -594,6 +594,8 @@ struct ath_softc {
- struct delayed_work wiphy_work;
- unsigned long wiphy_scheduler_int;
- int wiphy_scheduler_index;
-+ struct survey_info *cur_survey;
-+ struct survey_info survey[ATH9K_NUM_CHANNELS];
-
- struct tasklet_struct intr_tq;
- struct tasklet_struct bcon_tasklet;
---- a/drivers/net/wireless/ath/ath9k/main.c
-+++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -176,6 +176,44 @@ static void ath_start_ani(struct ath_com
- msecs_to_jiffies((u32)ah->config.ani_poll_interval));
- }
-
-+static void ath_update_survey_nf(struct ath_softc *sc, int channel)
-+{
-+ struct ath_hw *ah = sc->sc_ah;
-+ struct ath9k_channel *chan = &ah->channels[channel];
-+ struct survey_info *survey = &sc->survey[channel];
-+
-+ if (chan->noisefloor) {
-+ survey->filled |= SURVEY_INFO_NOISE_DBM;
-+ survey->noise = chan->noisefloor;
-+ }
-+}
-+
-+static void ath_update_survey_stats(struct ath_softc *sc)
-+{
-+ struct ath_hw *ah = sc->sc_ah;
-+ struct ath_common *common = ath9k_hw_common(ah);
-+ int pos = ah->curchan - &ah->channels[0];
-+ struct survey_info *survey = &sc->survey[pos];
-+ struct ath_cycle_counters *cc = &common->cc_survey;
-+ unsigned int div = common->clockrate * 1000;
-+
-+ ath_hw_cycle_counters_update(common);
-+
-+ if (cc->cycles > 0) {
-+ survey->filled |= SURVEY_INFO_CHANNEL_TIME |
-+ SURVEY_INFO_CHANNEL_TIME_BUSY |
-+ SURVEY_INFO_CHANNEL_TIME_RX |
-+ SURVEY_INFO_CHANNEL_TIME_TX;
-+ survey->channel_time += cc->cycles / div;
-+ survey->channel_time_busy += cc->rx_busy / div;
-+ survey->channel_time_rx += cc->rx_frame / div;
-+ survey->channel_time_tx += cc->tx_frame / div;
-+ }
-+ memset(cc, 0, sizeof(*cc));
-+
-+ ath_update_survey_nf(sc, pos);
-+}
-+
- /*
- * 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
-@@ -454,6 +492,7 @@ void ath_ani_calibrate(unsigned long dat
- if (aniflag) {
- spin_lock_irqsave(&common->cc_lock, flags);
- ath9k_hw_ani_monitor(ah, ah->curchan);
-+ ath_update_survey_stats(sc);
- spin_unlock_irqrestore(&common->cc_lock, flags);
- }
-
-@@ -1533,7 +1572,8 @@ static int ath9k_config(struct ieee80211
- {
- struct ath_wiphy *aphy = hw->priv;
- struct ath_softc *sc = aphy->sc;
-- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-+ struct ath_hw *ah = sc->sc_ah;
-+ struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_conf *conf = &hw->conf;
- bool disable_radio;
-
-@@ -1599,6 +1639,11 @@ static int ath9k_config(struct ieee80211
- if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- struct ieee80211_channel *curchan = hw->conf.channel;
- int pos = curchan->hw_value;
-+ int old_pos = -1;
-+ unsigned long flags;
-+
-+ if (ah->curchan)
-+ old_pos = ah->curchan - &ah->channels[0];
-
- aphy->chan_idx = pos;
- aphy->chan_is_ht = conf_is_ht(conf);
-@@ -1626,12 +1671,45 @@ static int ath9k_config(struct ieee80211
-
- ath_update_chainmask(sc, conf_is_ht(conf));
-
-+ /* update survey stats for the old channel before switching */
-+ spin_lock_irqsave(&common->cc_lock, flags);
-+ ath_update_survey_stats(sc);
-+ spin_unlock_irqrestore(&common->cc_lock, flags);
-+
-+ /*
-+ * If the operating channel changes, change the survey in-use flags
-+ * along with it.
-+ * Reset the survey data for the new channel, unless we're switching
-+ * back to the operating channel from an off-channel operation.
-+ */
-+ if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) &&
-+ sc->cur_survey != &sc->survey[pos]) {
-+
-+ if (sc->cur_survey)
-+ sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
-+
-+ sc->cur_survey = &sc->survey[pos];
-+
-+ memset(sc->cur_survey, 0, sizeof(struct survey_info));
-+ sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
-+ } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
-+ memset(&sc->survey[pos], 0, sizeof(struct survey_info));
-+ }
-+
- if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
- ath_print(common, ATH_DBG_FATAL,
- "Unable to set channel\n");
- mutex_unlock(&sc->mutex);
- return -EINVAL;
- }
-+
-+ /*
-+ * The most recent snapshot of channel->noisefloor for the old
-+ * channel is only available after the hardware reset. Copy it to
-+ * the survey stats now.
-+ */
-+ if (old_pos >= 0)
-+ ath_update_survey_nf(sc, old_pos);
- }
-
- skip_chan_change:
-@@ -2001,9 +2079,15 @@ static int ath9k_get_survey(struct ieee8
- {
- struct ath_wiphy *aphy = hw->priv;
- struct ath_softc *sc = aphy->sc;
-- struct ath_hw *ah = sc->sc_ah;
-+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ieee80211_supported_band *sband;
-- struct ath9k_channel *chan;
-+ struct ieee80211_channel *chan;
-+ unsigned long flags;
-+ int pos;
-+
-+ spin_lock_irqsave(&common->cc_lock, flags);
-+ if (idx == 0)
-+ ath_update_survey_stats(sc);
-
- sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ];
- if (sband && idx >= sband->n_channels) {
-@@ -2014,21 +2098,17 @@ static int ath9k_get_survey(struct ieee8
- if (!sband)
- sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
-
-- if (!sband || idx >= sband->n_channels)
-- return -ENOENT;
--
-- survey->channel = &sband->channels[idx];
-- chan = &ah->channels[survey->channel->hw_value];
-- survey->filled = 0;
--
-- if (chan == ah->curchan)
-- survey->filled |= SURVEY_INFO_IN_USE;
--
-- if (chan->noisefloor) {
-- survey->filled |= SURVEY_INFO_NOISE_DBM;
-- survey->noise = chan->noisefloor;
-+ if (!sband || idx >= sband->n_channels) {
-+ spin_unlock_irqrestore(&common->cc_lock, flags);
-+ return -ENOENT;
- }
-
-+ chan = &sband->channels[idx];
-+ pos = chan->hw_value;
-+ memcpy(survey, &sc->survey[pos], sizeof(*survey));
-+ survey->channel = chan;
-+ spin_unlock_irqrestore(&common->cc_lock, flags);
-+
- return 0;
- }
-
diff --git a/package/mac80211/patches/540-ath9k_rc_debugfs.patch b/package/mac80211/patches/540-ath9k_rc_debugfs.patch
new file mode 100644
index 0000000000..4e413f88d1
--- /dev/null
+++ b/package/mac80211/patches/540-ath9k_rc_debugfs.patch
@@ -0,0 +1,363 @@
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -378,95 +378,6 @@ static const struct file_operations fops
+ .owner = THIS_MODULE
+ };
+
+-void ath_debug_stat_rc(struct ath_softc *sc, int final_rate)
+-{
+- struct ath_rc_stats *stats;
+-
+- stats = &sc->debug.stats.rcstats[final_rate];
+- 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 * 1024 + 1;
+- buf = kmalloc(max, GFP_KERNEL);
+- if (buf == NULL)
+- return -ENOMEM;
+-
+- len += sprintf(buf, "%6s %6s %6s "
+- "%10s %10s %10s %10s\n",
+- "HT", "MCS", "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];
+- char mcs[5];
+- char htmode[5];
+- int used_mcs = 0, used_htmode = 0;
+-
+- if (WLAN_RC_PHY_HT(sc->cur_rate_table->info[i].phy)) {
+- used_mcs = snprintf(mcs, 5, "%d",
+- sc->cur_rate_table->info[i].ratecode);
+-
+- if (WLAN_RC_PHY_40(sc->cur_rate_table->info[i].phy))
+- used_htmode = snprintf(htmode, 5, "HT40");
+- else if (WLAN_RC_PHY_20(sc->cur_rate_table->info[i].phy))
+- used_htmode = snprintf(htmode, 5, "HT20");
+- else
+- used_htmode = snprintf(htmode, 5, "????");
+- }
+-
+- mcs[used_mcs] = '\0';
+- htmode[used_htmode] = '\0';
+-
+- len += snprintf(buf + len, max - len,
+- "%6s %6s %3u.%d: "
+- "%10u %10u %10u %10u\n",
+- htmode,
+- mcs,
+- ratekbps / 1000,
+- (ratekbps % 1000) / 100,
+- stats->success,
+- stats->retries,
+- stats->xretries,
+- stats->per);
+- }
+-
+- if (len > max)
+- len = max;
+-
+- 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) {
+@@ -1024,10 +935,6 @@ int ath9k_init_debug(struct ath_hw *ah)
+ sc, &fops_interrupt))
+ goto err;
+
+- if (!debugfs_create_file("rcstat", S_IRUSR, sc->debug.debugfs_phy,
+- sc, &fops_rcstat))
+- goto err;
+-
+ if (!debugfs_create_file("wiphy", S_IRUSR | S_IWUSR,
+ sc->debug.debugfs_phy, sc, &fops_wiphy))
+ goto err;
+--- a/drivers/net/wireless/ath/ath9k/debug.h
++++ b/drivers/net/wireless/ath/ath9k/debug.h
+@@ -80,13 +80,6 @@ struct ath_interrupt_stats {
+ u32 bb_watchdog;
+ };
+
+-struct ath_rc_stats {
+- u32 success;
+- u32 retries;
+- u32 xretries;
+- u8 per;
+-};
+-
+ /**
+ * struct ath_tx_stats - Statistics about TX
+ * @tx_pkts_all: No. of total frames transmitted, including ones that
+@@ -160,7 +153,6 @@ struct ath_rx_stats {
+
+ struct ath_stats {
+ struct ath_interrupt_stats istats;
+- struct ath_rc_stats rcstats[RATE_TABLE_SIZE];
+ struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES];
+ struct ath_rx_stats rxstats;
+ };
+@@ -177,12 +169,9 @@ void ath9k_exit_debug(struct ath_hw *ah)
+ 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, int final_rate);
+ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq,
+ struct ath_buf *bf, struct ath_tx_status *ts);
+ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs);
+-void ath_debug_stat_retries(struct ath_softc *sc, int rix,
+- int xretries, int retries, u8 per);
+
+ #else
+
+@@ -209,11 +198,6 @@ static inline void ath_debug_stat_interr
+ {
+ }
+
+-static inline void ath_debug_stat_rc(struct ath_softc *sc,
+- int final_rate)
+-{
+-}
+-
+ static inline void ath_debug_stat_tx(struct ath_softc *sc,
+ struct ath_txq *txq,
+ struct ath_buf *bf,
+@@ -226,11 +210,6 @@ static inline void ath_debug_stat_rx(str
+ {
+ }
+
+-static inline void ath_debug_stat_retries(struct ath_softc *sc, int rix,
+- int xretries, int retries, u8 per)
+-{
+-}
+-
+ #endif /* CONFIG_ATH9K_DEBUGFS */
+
+ #endif /* DEBUG_H */
+--- a/drivers/net/wireless/ath/ath9k/rc.c
++++ b/drivers/net/wireless/ath/ath9k/rc.c
+@@ -1026,6 +1026,16 @@ static bool ath_rc_update_per(struct ath
+ return state_change;
+ }
+
++static void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix,
++ int xretries, int retries, u8 per)
++{
++ struct ath_rc_stats *stats = &rc->rcstats[rix];
++
++ stats->xretries += xretries;
++ stats->retries += retries;
++ stats->per = per;
++}
++
+ /* 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. */
+
+@@ -1098,7 +1108,7 @@ static void ath_rc_update_ht(struct ath_
+ ath_rc_priv->per_down_time = now_msec;
+ }
+
+- ath_debug_stat_retries(sc, tx_rate, xretries, retries,
++ ath_debug_stat_retries(ath_rc_priv, tx_rate, xretries, retries,
+ ath_rc_priv->per[tx_rate]);
+
+ }
+@@ -1294,6 +1304,7 @@ static void ath_rc_init(struct ath_softc
+ 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;
++ ath_rc_priv->rate_table = rate_table;
+
+ ath_print(common, ATH_DBG_CONFIG,
+ "RC Initialized with capabilities: 0x%x\n",
+@@ -1340,6 +1351,15 @@ static bool ath_tx_aggr_check(struct ath
+ /* mac80211 Rate Control callbacks */
+ /***********************************/
+
++static void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate)
++{
++ struct ath_rc_stats *stats;
++
++ stats = &rc->rcstats[final_rate];
++ stats->success++;
++}
++
++
+ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta,
+ struct sk_buff *skb)
+@@ -1419,7 +1439,7 @@ static void ath_tx_status(void *priv, st
+ }
+ }
+
+- ath_debug_stat_rc(sc, ath_rc_get_rateindex(sc->cur_rate_table,
++ ath_debug_stat_rc(ath_rc_priv, ath_rc_get_rateindex(sc->cur_rate_table,
+ &tx_info->status.rates[final_ts_idx]));
+ }
+
+@@ -1521,6 +1541,94 @@ static void ath_rate_update(void *priv,
+ }
+ }
+
++#ifdef CONFIG_ATH9K_DEBUGFS
++
++static int ath9k_debugfs_open(struct inode *inode, struct file *file)
++{
++ file->private_data = inode->i_private;
++ return 0;
++}
++
++static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath_rate_priv *rc = file->private_data;
++ char *buf;
++ unsigned int len = 0, max;
++ int i = 0;
++ ssize_t retval;
++
++ if (rc->rate_table == NULL)
++ return 0;
++
++ max = 80 + rc->rate_table->rate_cnt * 1024 + 1;
++ buf = kmalloc(max, GFP_KERNEL);
++ if (buf == NULL)
++ return -ENOMEM;
++
++ len += sprintf(buf, "%6s %6s %6s "
++ "%10s %10s %10s %10s\n",
++ "HT", "MCS", "Rate",
++ "Success", "Retries", "XRetries", "PER");
++
++ for (i = 0; i < rc->rate_table->rate_cnt; i++) {
++ u32 ratekbps = rc->rate_table->info[i].ratekbps;
++ struct ath_rc_stats *stats = &rc->rcstats[i];
++ char mcs[5];
++ char htmode[5];
++ int used_mcs = 0, used_htmode = 0;
++
++ if (WLAN_RC_PHY_HT(rc->rate_table->info[i].phy)) {
++ used_mcs = snprintf(mcs, 5, "%d",
++ rc->rate_table->info[i].ratecode);
++
++ if (WLAN_RC_PHY_40(rc->rate_table->info[i].phy))
++ used_htmode = snprintf(htmode, 5, "HT40");
++ else if (WLAN_RC_PHY_20(rc->rate_table->info[i].phy))
++ used_htmode = snprintf(htmode, 5, "HT20");
++ else
++ used_htmode = snprintf(htmode, 5, "????");
++ }
++
++ mcs[used_mcs] = '\0';
++ htmode[used_htmode] = '\0';
++
++ len += snprintf(buf + len, max - len,
++ "%6s %6s %3u.%d: "
++ "%10u %10u %10u %10u\n",
++ htmode,
++ mcs,
++ ratekbps / 1000,
++ (ratekbps % 1000) / 100,
++ stats->success,
++ stats->retries,
++ stats->xretries,
++ stats->per);
++ }
++
++ if (len > max)
++ len = max;
++
++ 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 void ath_rate_add_sta_debugfs(void *priv, void *priv_sta,
++ struct dentry *dir)
++{
++ struct ath_rate_priv *rc = priv_sta;
++ debugfs_create_file("rc_stats", S_IRUGO, dir, rc, &fops_rcstat);
++}
++
++#endif /* CONFIG_ATH9K_DEBUGFS */
++
+ static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+ {
+ struct ath_wiphy *aphy = hw->priv;
+@@ -1567,6 +1675,9 @@ static struct rate_control_ops ath_rate_
+ .free = ath_rate_free,
+ .alloc_sta = ath_rate_alloc_sta,
+ .free_sta = ath_rate_free_sta,
++#ifdef CONFIG_ATH9K_DEBUGFS
++ .add_sta_debugfs = ath_rate_add_sta_debugfs,
++#endif
+ };
+
+ int ath_rate_control_register(void)
+--- a/drivers/net/wireless/ath/ath9k/rc.h
++++ b/drivers/net/wireless/ath/ath9k/rc.h
+@@ -176,6 +176,13 @@ struct ath_rateset {
+ u8 rs_rates[ATH_RATE_MAX];
+ };
+
++struct ath_rc_stats {
++ u32 success;
++ u32 retries;
++ u32 xretries;
++ u8 per;
++};
++
+ /**
+ * struct ath_rate_priv - Rate Control priv data
+ * @state: RC state
+@@ -212,6 +219,10 @@ struct ath_rate_priv {
+ struct ath_rateset neg_rates;
+ struct ath_rateset neg_ht_rates;
+ struct ath_rate_softc *asc;
++ const struct ath_rate_table *rate_table;
++
++ struct dentry *debugfs_rcstats;
++ struct ath_rc_stats rcstats[RATE_TABLE_SIZE];
+ };
+
+ #define ATH_TX_INFO_FRAME_TYPE_INTERNAL (1 << 0)
diff --git a/package/mac80211/patches/541-ath9k_rc_rate_table_cleanup.patch b/package/mac80211/patches/541-ath9k_rc_rate_table_cleanup.patch
new file mode 100644
index 0000000000..b3c0413178
--- /dev/null
+++ b/package/mac80211/patches/541-ath9k_rc_rate_table_cleanup.patch
@@ -0,0 +1,158 @@
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -624,8 +624,6 @@ struct ath_softc {
+ struct ath_rx rx;
+ struct ath_tx tx;
+ struct ath_beacon beacon;
+- const struct ath_rate_table *cur_rate_table;
+- enum wireless_mode cur_rate_mode;
+ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+
+ struct ath_led radio_led;
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -19,36 +19,6 @@
+ #include "ath9k.h"
+ #include "btcoex.h"
+
+-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_mode = ATH9K_MODE_11NG_HT20;
+- else if (conf_is_ht40_minus(conf))
+- sc->cur_rate_mode = ATH9K_MODE_11NG_HT40MINUS;
+- else if (conf_is_ht40_plus(conf))
+- sc->cur_rate_mode = ATH9K_MODE_11NG_HT40PLUS;
+- else
+- sc->cur_rate_mode = ATH9K_MODE_11G;
+- break;
+- case IEEE80211_BAND_5GHZ:
+- if (conf_is_ht20(conf))
+- sc->cur_rate_mode = ATH9K_MODE_11NA_HT20;
+- else if (conf_is_ht40_minus(conf))
+- sc->cur_rate_mode = ATH9K_MODE_11NA_HT40MINUS;
+- else if (conf_is_ht40_plus(conf))
+- sc->cur_rate_mode = ATH9K_MODE_11NA_HT40PLUS;
+- else
+- sc->cur_rate_mode = 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;
+@@ -307,7 +277,6 @@ int ath_set_channel(struct ath_softc *sc
+ goto ps_restore;
+ }
+
+- ath_cache_conf_rate(sc, &hw->conf);
+ ath_update_txpow(sc);
+ ath9k_hw_set_interrupts(ah, ah->imask);
+
+@@ -1014,8 +983,6 @@ int ath_reset(struct ath_softc *sc, bool
+ * 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) || !(sc->sc_flags & (SC_OP_OFFCHANNEL)))
+@@ -1222,8 +1189,6 @@ static int ath9k_start(struct ieee80211_
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
+ ah->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 */
+--- a/drivers/net/wireless/ath/ath9k/rc.c
++++ b/drivers/net/wireless/ath/ath9k/rc.c
+@@ -791,7 +791,7 @@ static void ath_get_rate(void *priv, str
+ */
+ try_per_rate = 4;
+
+- rate_table = sc->cur_rate_table;
++ rate_table = ath_rc_priv->rate_table;
+ rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe);
+
+ /*
+@@ -1048,7 +1048,7 @@ static void ath_rc_update_ht(struct ath_
+ int rate;
+ u8 last_per;
+ bool state_change = false;
+- const struct ath_rate_table *rate_table = sc->cur_rate_table;
++ const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
+ int size = ath_rc_priv->rate_table_size;
+
+ if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt))
+@@ -1150,7 +1150,7 @@ static void ath_rc_tx_status(struct ath_
+ u8 flags;
+ u32 i = 0, rix;
+
+- rate_table = sc->cur_rate_table;
++ rate_table = ath_rc_priv->rate_table;
+
+ /*
+ * If the first rate is not the final index, there
+@@ -1231,7 +1231,6 @@ struct ath_rate_table *ath_choose_rate_t
+ ath_print(common, ATH_DBG_CONFIG,
+ "Choosing rate table for mode: %d\n", mode);
+
+- sc->cur_rate_mode = mode;
+ return hw_rate_table[mode];
+ }
+
+@@ -1303,7 +1302,6 @@ static void ath_rc_init(struct ath_softc
+ 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;
+ ath_rc_priv->rate_table = rate_table;
+
+ ath_print(common, ATH_DBG_CONFIG,
+@@ -1439,8 +1437,9 @@ static void ath_tx_status(void *priv, st
+ }
+ }
+
+- ath_debug_stat_rc(ath_rc_priv, ath_rc_get_rateindex(sc->cur_rate_table,
+- &tx_info->status.rates[final_ts_idx]));
++ ath_debug_stat_rc(ath_rc_priv,
++ ath_rc_get_rateindex(ath_rc_priv->rate_table,
++ &tx_info->status.rates[final_ts_idx]));
+ }
+
+ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
+@@ -1480,14 +1479,8 @@ static void ath_rate_init(void *priv, st
+
+ /* 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 {
+- rate_table = hw_rate_table[sc->cur_rate_mode];
+- }
++ rate_table = ath_choose_rate_table(sc, sband->band,
++ sta->ht_cap.ht_supported, is_cw40);
+
+ ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi);
+ ath_rc_init(sc, priv_sta, sband, sta, rate_table);
+@@ -1536,7 +1529,6 @@ static void ath_rate_update(void *priv,
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG,
+ "Operating HT Bandwidth changed to: %d\n",
+ sc->hw->conf.channel_type);
+- sc->cur_rate_table = hw_rate_table[sc->cur_rate_mode];
+ }
+ }
+ }
diff --git a/package/mac80211/patches/542-ath9k_no_mode_idx.patch b/package/mac80211/patches/542-ath9k_no_mode_idx.patch
new file mode 100644
index 0000000000..ae6854637d
--- /dev/null
+++ b/package/mac80211/patches/542-ath9k_no_mode_idx.patch
@@ -0,0 +1,247 @@
+--- a/drivers/net/wireless/ath/ath9k/rc.c
++++ b/drivers/net/wireless/ath/ath9k/rc.c
+@@ -378,17 +378,6 @@ static const struct ath_rate_table ar541
+ 0, /* Phy rates allowed initially */
+ };
+
+-static const struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX] = {
+- [ATH9K_MODE_11A] = &ar5416_11a_ratetable,
+- [ATH9K_MODE_11G] = &ar5416_11g_ratetable,
+- [ATH9K_MODE_11NA_HT20] = &ar5416_11na_ratetable,
+- [ATH9K_MODE_11NG_HT20] = &ar5416_11ng_ratetable,
+- [ATH9K_MODE_11NA_HT40PLUS] = &ar5416_11na_ratetable,
+- [ATH9K_MODE_11NA_HT40MINUS] = &ar5416_11na_ratetable,
+- [ATH9K_MODE_11NG_HT40PLUS] = &ar5416_11ng_ratetable,
+- [ATH9K_MODE_11NG_HT40MINUS] = &ar5416_11ng_ratetable,
+-};
+-
+ static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table,
+ struct ieee80211_tx_rate *rate);
+
+@@ -1200,38 +1189,23 @@ static void ath_rc_tx_status(struct ath_
+ 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)
++ bool is_ht)
+ {
+- int mode = 0;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ 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;
++ return &ar5416_11ng_ratetable;
++ return &ar5416_11g_ratetable;
+ 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;
++ return &ar5416_11na_ratetable;
++ return &ar5416_11a_ratetable;
+ default:
+ ath_print(common, ATH_DBG_CONFIG, "Invalid band\n");
+ return NULL;
+ }
+-
+- BUG_ON(mode >= ATH9K_MODE_MAX);
+-
+- ath_print(common, ATH_DBG_CONFIG,
+- "Choosing rate table for mode: %d\n", mode);
+-
+- return hw_rate_table[mode];
+ }
+
+ static void ath_rc_init(struct ath_softc *sc,
+@@ -1480,7 +1454,7 @@ static void ath_rate_init(void *priv, st
+ /* Choose rate table first */
+
+ rate_table = ath_choose_rate_table(sc, sband->band,
+- sta->ht_cap.ht_supported, is_cw40);
++ sta->ht_cap.ht_supported);
+
+ ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi);
+ ath_rc_init(sc, priv_sta, sband, sta, rate_table);
+@@ -1520,8 +1494,7 @@ static void ath_rate_update(void *priv,
+
+ if ((local_cw40 != oper_cw40) || (local_sgi != oper_sgi)) {
+ rate_table = ath_choose_rate_table(sc, sband->band,
+- sta->ht_cap.ht_supported,
+- oper_cw40);
++ sta->ht_cap.ht_supported);
+ ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta,
+ oper_cw40, oper_sgi);
+ ath_rc_init(sc, priv_sta, sband, sta, rate_table);
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -1812,37 +1812,11 @@ int ath9k_hw_fill_cap_info(struct ath_hw
+ return -EINVAL;
+ }
+
+- bitmap_zero(pCap->wireless_modes, ATH9K_MODE_MAX);
++ if (eeval & AR5416_OPFLAGS_11A)
++ pCap->hw_caps |= ATH9K_HW_CAP_5GHZ;
+
+- 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_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);
+- }
+- }
+- }
++ if (eeval & AR5416_OPFLAGS_11G)
++ pCap->hw_caps |= ATH9K_HW_CAP_2GHZ;
+
+ pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK);
+ /*
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -164,18 +164,6 @@ enum ath_ini_subsys {
+ ATH_INI_NUM_SPLIT,
+ };
+
+-enum wireless_mode {
+- ATH9K_MODE_11A = 0,
+- ATH9K_MODE_11G,
+- ATH9K_MODE_11NA_HT20,
+- ATH9K_MODE_11NG_HT20,
+- ATH9K_MODE_11NA_HT40PLUS,
+- ATH9K_MODE_11NA_HT40MINUS,
+- ATH9K_MODE_11NG_HT40PLUS,
+- ATH9K_MODE_11NG_HT40MINUS,
+- ATH9K_MODE_MAX,
+-};
+-
+ enum ath9k_hw_caps {
+ ATH9K_HW_CAP_HT = BIT(0),
+ ATH9K_HW_CAP_RFSILENT = BIT(1),
+@@ -190,11 +178,12 @@ enum ath9k_hw_caps {
+ ATH9K_HW_CAP_SGI_20 = BIT(10),
+ ATH9K_HW_CAP_PAPRD = BIT(11),
+ ATH9K_HW_CAP_ANT_DIV_COMB = BIT(12),
++ ATH9K_HW_CAP_2GHZ = BIT(13),
++ ATH9K_HW_CAP_5GHZ = BIT(14),
+ };
+
+ 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;
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -486,7 +486,7 @@ static int ath9k_init_channels_rates(str
+ ARRAY_SIZE(ath9k_5ghz_chantable) !=
+ ATH9K_NUM_CHANNELS);
+
+- if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) {
++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
+ channels = kmemdup(ath9k_2ghz_chantable,
+ sizeof(ath9k_2ghz_chantable), GFP_KERNEL);
+ if (!channels)
+@@ -501,7 +501,7 @@ static int ath9k_init_channels_rates(str
+ ARRAY_SIZE(ath9k_legacy_rates);
+ }
+
+- if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) {
++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
+ channels = kmemdup(ath9k_5ghz_chantable,
+ sizeof(ath9k_5ghz_chantable), GFP_KERNEL);
+ if (!channels) {
+@@ -690,17 +690,17 @@ void ath9k_set_hw_capab(struct ath_softc
+ hw->rate_control_algorithm = "ath9k_rate_control";
+ #endif
+
+- if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes))
++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &sc->sbands[IEEE80211_BAND_2GHZ];
+- if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &sc->sbands[IEEE80211_BAND_5GHZ];
+
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
+- if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes))
++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
+ setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
+- if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
+ setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
+ }
+
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+@@ -566,7 +566,7 @@ static void ath9k_init_crypto(struct ath
+
+ static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv)
+ {
+- if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) {
++ if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
+ priv->sbands[IEEE80211_BAND_2GHZ].channels =
+ ath9k_2ghz_channels;
+ priv->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
+@@ -577,7 +577,7 @@ static void ath9k_init_channels_rates(st
+ ARRAY_SIZE(ath9k_legacy_rates);
+ }
+
+- if (test_bit(ATH9K_MODE_11A, priv->ah->caps.wireless_modes)) {
++ if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
+ priv->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_channels;
+ priv->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
+ priv->sbands[IEEE80211_BAND_5GHZ].n_channels =
+@@ -740,18 +740,18 @@ static void ath9k_set_hw_capab(struct at
+ hw->extra_tx_headroom = sizeof(struct tx_frame_hdr) +
+ sizeof(struct htc_frame_hdr) + 4;
+
+- if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes))
++ if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &priv->sbands[IEEE80211_BAND_2GHZ];
+- if (test_bit(ATH9K_MODE_11A, priv->ah->caps.wireless_modes))
++ if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &priv->sbands[IEEE80211_BAND_5GHZ];
+
+ if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
+- if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes))
++ if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
+ setup_ht_cap(priv,
+ &priv->sbands[IEEE80211_BAND_2GHZ].ht_cap);
+- if (test_bit(ATH9K_MODE_11A, priv->ah->caps.wireless_modes))
++ if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
+ setup_ht_cap(priv,
+ &priv->sbands[IEEE80211_BAND_5GHZ].ht_cap);
+ }
diff --git a/package/mac80211/patches/550-ath9k_interrupt_mask_optimization.patch b/package/mac80211/patches/550-ath9k_interrupt_mask_optimization.patch
new file mode 100644
index 0000000000..d6a1d7525f
--- /dev/null
+++ b/package/mac80211/patches/550-ath9k_interrupt_mask_optimization.patch
@@ -0,0 +1,282 @@
+--- a/drivers/net/wireless/ath/ath9k/mac.c
++++ b/drivers/net/wireless/ath/ath9k/mac.c
+@@ -117,12 +117,11 @@ EXPORT_SYMBOL(ath9k_hw_numtxpending);
+ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
+ {
+ u32 txcfg, curLevel, newLevel;
+- enum ath9k_int omask;
+
+ if (ah->tx_trig_level >= ah->config.max_txtrig_level)
+ return false;
+
+- omask = ath9k_hw_set_interrupts(ah, ah->imask & ~ATH9K_INT_GLOBAL);
++ ath9k_hw_disable_interrupts(ah);
+
+ txcfg = REG_READ(ah, AR_TXCFG);
+ curLevel = MS(txcfg, AR_FTRIG);
+@@ -136,7 +135,7 @@ bool ath9k_hw_updatetxtriglevel(struct a
+ REG_WRITE(ah, AR_TXCFG,
+ (txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG));
+
+- ath9k_hw_set_interrupts(ah, omask);
++ ath9k_hw_enable_interrupts(ah);
+
+ ah->tx_trig_level = newLevel;
+
+@@ -849,28 +848,59 @@ bool ath9k_hw_intrpend(struct ath_hw *ah
+ }
+ EXPORT_SYMBOL(ath9k_hw_intrpend);
+
+-enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah,
+- enum ath9k_int ints)
++void ath9k_hw_disable_interrupts(struct ath_hw *ah)
++{
++ struct ath_common *common = ath9k_hw_common(ah);
++
++ ath_print(common, 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);
++ }
++}
++EXPORT_SYMBOL(ath9k_hw_disable_interrupts);
++
++void ath9k_hw_enable_interrupts(struct ath_hw *ah)
++{
++ struct ath_common *common = ath9k_hw_common(ah);
++
++ if (!(ah->imask & ATH9K_INT_GLOBAL))
++ return;
++
++ ath_print(common, 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);
++ }
++ ath_print(common, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
++ REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
++}
++EXPORT_SYMBOL(ath9k_hw_enable_interrupts);
++
++void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
+ {
+ enum ath9k_int omask = ah->imask;
+ u32 mask, mask2;
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
+ struct ath_common *common = ath9k_hw_common(ah);
+
+- ath_print(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);
+-
+- if (omask & ATH9K_INT_GLOBAL) {
+- ath_print(common, 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);
++ if (!(ints & ATH9K_INT_GLOBAL))
++ ath9k_hw_enable_interrupts(ah);
+
+- REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
+- (void) REG_READ(ah, AR_INTR_SYNC_ENABLE);
+- }
+- }
++ ath_print(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);
+
+ /* TODO: global int Ref count */
+ mask = ints & ATH9K_INT_COMMON;
+@@ -946,24 +976,8 @@ enum ath9k_int ath9k_hw_set_interrupts(s
+ REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER);
+ }
+
+- if (ints & ATH9K_INT_GLOBAL) {
+- ath_print(common, 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);
+- }
+- ath_print(common, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
+- REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
+- }
++ ath9k_hw_enable_interrupts(ah);
+
+- return omask;
++ return;
+ }
+ EXPORT_SYMBOL(ath9k_hw_set_interrupts);
+--- a/drivers/net/wireless/ath/ath9k/mac.h
++++ b/drivers/net/wireless/ath/ath9k/mac.h
+@@ -669,6 +669,7 @@ enum ath9k_key_type {
+
+ struct ath_hw;
+ struct ath9k_channel;
++enum ath9k_int;
+
+ u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q);
+ void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp);
+@@ -700,8 +701,9 @@ int ath9k_hw_beaconq_setup(struct ath_hw
+
+ /* Interrupt Handling */
+ bool ath9k_hw_intrpend(struct ath_hw *ah);
+-enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah,
+- enum ath9k_int ints);
++void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints);
++void ath9k_hw_enable_interrupts(struct ath_hw *ah);
++void ath9k_hw_disable_interrupts(struct ath_hw *ah);
+
+ void ar9002_hw_attach_mac_ops(struct ath_hw *ah);
+
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -237,7 +237,7 @@ int ath_set_channel(struct ath_softc *sc
+ * hardware at the new frequency, and then re-enable
+ * the relevant bits of the h/w.
+ */
+- ath9k_hw_set_interrupts(ah, 0);
++ ath9k_hw_disable_interrupts(ah);
+ ath_drain_all_txq(sc, false);
+ stopped = ath_stoprecv(sc);
+
+@@ -644,7 +644,7 @@ void ath9k_tasklet(unsigned long data)
+ ath_gen_timer_isr(sc->sc_ah);
+
+ /* re-enable hardware interrupt */
+- ath9k_hw_set_interrupts(ah, ah->imask);
++ ath9k_hw_enable_interrupts(ah);
+ ath9k_ps_restore(sc);
+ }
+
+@@ -743,7 +743,7 @@ irqreturn_t ath_isr(int irq, void *dev)
+ * interrupt; otherwise it will continue to
+ * fire.
+ */
+- ath9k_hw_set_interrupts(ah, 0);
++ ath9k_hw_disable_interrupts(ah);
+ /*
+ * Let the hal handle the event. We assume
+ * it will clear whatever condition caused
+@@ -752,7 +752,7 @@ irqreturn_t ath_isr(int irq, void *dev)
+ spin_lock(&common->cc_lock);
+ ath9k_hw_proc_mib_event(ah);
+ spin_unlock(&common->cc_lock);
+- ath9k_hw_set_interrupts(ah, ah->imask);
++ ath9k_hw_enable_interrupts(ah);
+ }
+
+ if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
+@@ -769,8 +769,8 @@ chip_reset:
+ ath_debug_stat_interrupt(sc, status);
+
+ if (sched) {
+- /* turn off every interrupt except SWBA */
+- ath9k_hw_set_interrupts(ah, (ah->imask & ATH9K_INT_SWBA));
++ /* turn off every interrupt */
++ ath9k_hw_disable_interrupts(ah);
+ tasklet_schedule(&sc->intr_tq);
+ }
+
+@@ -925,7 +925,7 @@ void ath_radio_disable(struct ath_softc
+ }
+
+ /* Disable interrupts */
+- ath9k_hw_set_interrupts(ah, 0);
++ ath9k_hw_disable_interrupts(ah);
+
+ ath_drain_all_txq(sc, false); /* clear pending tx frames */
+ ath_stoprecv(sc); /* turn off frame recv */
+@@ -962,7 +962,7 @@ int ath_reset(struct ath_softc *sc, bool
+
+ ieee80211_stop_queues(hw);
+
+- ath9k_hw_set_interrupts(ah, 0);
++ ath9k_hw_disable_interrupts(ah);
+ ath_drain_all_txq(sc, retry_tx);
+ ath_stoprecv(sc);
+ ath_flushrecv(sc);
+@@ -1367,7 +1367,7 @@ static void ath9k_stop(struct ieee80211_
+
+ /* make sure h/w will not generate any interrupt
+ * before setting the invalid flag. */
+- ath9k_hw_set_interrupts(ah, 0);
++ ath9k_hw_disable_interrupts(ah);
+
+ if (!(sc->sc_flags & SC_OP_INVALID)) {
+ ath_drain_all_txq(sc, false);
+--- a/drivers/net/wireless/ath/ath9k/beacon.c
++++ b/drivers/net/wireless/ath/ath9k/beacon.c
+@@ -500,10 +500,10 @@ static void ath_beacon_config_ap(struct
+
+ /* Set the computed AP beacon timers */
+
+- ath9k_hw_set_interrupts(ah, 0);
++ ath9k_hw_disable_interrupts(ah);
+ ath9k_beacon_init(sc, nexttbtt, intval);
+ sc->beacon.bmisscnt = 0;
+- ath9k_hw_set_interrupts(ah, ah->imask);
++ ath9k_hw_enable_interrupts(ah);
+
+ /* Clear the reset TSF flag, so that subsequent beacon updation
+ will not reset the HW TSF. */
+@@ -635,7 +635,7 @@ static void ath_beacon_config_sta(struct
+
+ /* Set the computed STA beacon timers */
+
+- ath9k_hw_set_interrupts(ah, 0);
++ ath9k_hw_disable_interrupts(ah);
+ ath9k_hw_set_sta_beacon_timers(ah, &bs);
+ ah->imask |= ATH9K_INT_BMISS;
+ ath9k_hw_set_interrupts(ah, ah->imask);
+@@ -683,10 +683,10 @@ static void ath_beacon_config_adhoc(stru
+
+ /* Set the computed ADHOC beacon timers */
+
+- ath9k_hw_set_interrupts(ah, 0);
++ ath9k_hw_disable_interrupts(ah);
+ ath9k_beacon_init(sc, nexttbtt, intval);
+ sc->beacon.bmisscnt = 0;
+- ath9k_hw_set_interrupts(ah, ah->imask);
++ ath9k_hw_enable_interrupts(ah);
+ }
+
+ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
+--- a/drivers/net/wireless/ath/ath9k/gpio.c
++++ b/drivers/net/wireless/ath/ath9k/gpio.c
+@@ -275,7 +275,7 @@ static void ath9k_gen_timer_start(struct
+ ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period);
+
+ if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
+- ath9k_hw_set_interrupts(ah, 0);
++ ath9k_hw_disable_interrupts(ah);
+ ah->imask |= ATH9K_INT_GENTIMER;
+ ath9k_hw_set_interrupts(ah, ah->imask);
+ }
+@@ -289,7 +289,7 @@ static void ath9k_gen_timer_stop(struct
+
+ /* if no timer is enabled, turn off interrupt mask */
+ if (timer_table->timer_mask.val == 0) {
+- ath9k_hw_set_interrupts(ah, 0);
++ ath9k_hw_disable_interrupts(ah);
+ ah->imask &= ~ATH9K_INT_GENTIMER;
+ ath9k_hw_set_interrupts(ah, ah->imask);
+ }
diff --git a/package/mac80211/patches/551-ath9k_isr_optimization.patch b/package/mac80211/patches/551-ath9k_isr_optimization.patch
new file mode 100644
index 0000000000..dc267f33b0
--- /dev/null
+++ b/package/mac80211/patches/551-ath9k_isr_optimization.patch
@@ -0,0 +1,60 @@
+--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+@@ -90,13 +90,10 @@ static bool ar9002_hw_get_isr(struct ath
+
+ *masked = isr & ATH9K_INT_COMMON;
+
+- if (ah->config.rx_intr_mitigation) {
+- if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM))
+- *masked |= ATH9K_INT_RX;
+- }
+-
+- if (isr & (AR_ISR_RXOK | AR_ISR_RXERR))
++ if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM |
++ AR_ISR_RXOK | AR_ISR_RXERR))
+ *masked |= ATH9K_INT_RX;
++
+ if (isr &
+ (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR |
+ AR_ISR_TXEOL)) {
+@@ -118,14 +115,6 @@ static bool ar9002_hw_get_isr(struct ath
+ "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;
+ }
+
+@@ -136,17 +125,18 @@ static bool ar9002_hw_get_isr(struct ath
+ u32 s5_s;
+
+ s5_s = REG_READ(ah, AR_ISR_S5_S);
+- if (isr & AR_ISR_GENTMR) {
+- ah->intr_gen_timer_trigger =
++ ah->intr_gen_timer_trigger =
+ MS(s5_s, AR_ISR_S5_GENTIMER_TRIG);
+
+- ah->intr_gen_timer_thresh =
+- MS(s5_s, AR_ISR_S5_GENTIMER_THRESH);
++ ah->intr_gen_timer_thresh =
++ MS(s5_s, AR_ISR_S5_GENTIMER_THRESH);
+
+- if (ah->intr_gen_timer_trigger)
+- *masked |= ATH9K_INT_GENTIMER;
++ if (ah->intr_gen_timer_trigger)
++ *masked |= ATH9K_INT_GENTIMER;
+
+- }
++ if ((s5_s & AR_ISR_S5_TIM_TIMER) &&
++ !(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
++ *masked |= ATH9K_INT_TIM_TIMER;
+ }
+
+ if (sync_cause) {
diff --git a/package/mac80211/patches/552-ath9k_txdesc_optimization.patch b/package/mac80211/patches/552-ath9k_txdesc_optimization.patch
new file mode 100644
index 0000000000..75d9f653d9
--- /dev/null
+++ b/package/mac80211/patches/552-ath9k_txdesc_optimization.patch
@@ -0,0 +1,248 @@
+--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+@@ -208,77 +208,68 @@ static int ar9002_hw_proc_txdesc(struct
+ struct ath_tx_status *ts)
+ {
+ struct ar5416_desc *ads = AR5416DESC(ds);
++ u32 status;
+
+- if ((ads->ds_txstatus9 & AR_TxDone) == 0)
++ status = ACCESS_ONCE(ads->ds_txstatus9);
++ if ((status & AR_TxDone) == 0)
+ return -EINPROGRESS;
+
+- ts->ts_seqnum = MS(ads->ds_txstatus9, AR_SeqNum);
+ ts->ts_tstamp = ads->AR_SendTimestamp;
+ ts->ts_status = 0;
+ ts->ts_flags = 0;
+
+- if (ads->ds_txstatus1 & AR_FrmXmitOK)
++ if (status & AR_TxOpExceeded)
++ ts->ts_status |= ATH9K_TXERR_XTXOP;
++ ts->tid = MS(status, AR_TxTid);
++ ts->ts_rateindex = MS(status, AR_FinalTxIdx);
++ ts->ts_seqnum = MS(status, AR_SeqNum);
++
++ status = ACCESS_ONCE(ads->ds_txstatus0);
++ ts->ts_rssi_ctl0 = MS(status, AR_TxRSSIAnt00);
++ ts->ts_rssi_ctl1 = MS(status, AR_TxRSSIAnt01);
++ ts->ts_rssi_ctl2 = MS(status, AR_TxRSSIAnt02);
++ if (status & AR_TxBaStatus) {
++ ts->ts_flags |= ATH9K_TX_BA;
++ ts->ba_low = ads->AR_BaBitmapLow;
++ ts->ba_high = ads->AR_BaBitmapHigh;
++ }
++
++ status = ACCESS_ONCE(ads->ds_txstatus1);
++ if (status & AR_FrmXmitOK)
+ ts->ts_status |= ATH9K_TX_ACKED;
+- if (ads->ds_txstatus1 & AR_ExcessiveRetries)
++ if (status & AR_ExcessiveRetries)
+ ts->ts_status |= ATH9K_TXERR_XRETRY;
+- if (ads->ds_txstatus1 & AR_Filtered)
++ if (status & AR_Filtered)
+ ts->ts_status |= ATH9K_TXERR_FILT;
+- if (ads->ds_txstatus1 & AR_FIFOUnderrun) {
++ if (status & AR_FIFOUnderrun) {
+ ts->ts_status |= ATH9K_TXERR_FIFO;
+ ath9k_hw_updatetxtriglevel(ah, true);
+ }
+- if (ads->ds_txstatus9 & AR_TxOpExceeded)
+- ts->ts_status |= ATH9K_TXERR_XTXOP;
+- if (ads->ds_txstatus1 & AR_TxTimerExpired)
++ if (status & AR_TxTimerExpired)
+ ts->ts_status |= ATH9K_TXERR_TIMER_EXPIRED;
+-
+- if (ads->ds_txstatus1 & AR_DescCfgErr)
++ if (status & AR_DescCfgErr)
+ ts->ts_flags |= ATH9K_TX_DESC_CFG_ERR;
+- if (ads->ds_txstatus1 & AR_TxDataUnderrun) {
++ if (status & AR_TxDataUnderrun) {
+ ts->ts_flags |= ATH9K_TX_DATA_UNDERRUN;
+ ath9k_hw_updatetxtriglevel(ah, true);
+ }
+- if (ads->ds_txstatus1 & AR_TxDelimUnderrun) {
++ if (status & AR_TxDelimUnderrun) {
+ ts->ts_flags |= ATH9K_TX_DELIM_UNDERRUN;
+ ath9k_hw_updatetxtriglevel(ah, true);
+ }
+- if (ads->ds_txstatus0 & AR_TxBaStatus) {
+- ts->ts_flags |= ATH9K_TX_BA;
+- ts->ba_low = ads->AR_BaBitmapLow;
+- ts->ba_high = ads->AR_BaBitmapHigh;
+- }
+-
+- ts->ts_rateindex = MS(ads->ds_txstatus9, AR_FinalTxIdx);
+- switch (ts->ts_rateindex) {
+- case 0:
+- ts->ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate0);
+- break;
+- case 1:
+- ts->ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate1);
+- break;
+- case 2:
+- ts->ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate2);
+- break;
+- case 3:
+- ts->ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate3);
+- break;
+- }
++ ts->ts_shortretry = MS(status, AR_RTSFailCnt);
++ ts->ts_longretry = MS(status, AR_DataFailCnt);
++ ts->ts_virtcol = MS(status, AR_VirtRetryCnt);
++
++ status = ACCESS_ONCE(ads->ds_txstatus5);
++ ts->ts_rssi = MS(status, AR_TxRSSICombined);
++ ts->ts_rssi_ext0 = MS(status, AR_TxRSSIAnt10);
++ ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11);
++ ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12);
+
+- ts->ts_rssi = MS(ads->ds_txstatus5, AR_TxRSSICombined);
+- ts->ts_rssi_ctl0 = MS(ads->ds_txstatus0, AR_TxRSSIAnt00);
+- ts->ts_rssi_ctl1 = MS(ads->ds_txstatus0, AR_TxRSSIAnt01);
+- ts->ts_rssi_ctl2 = MS(ads->ds_txstatus0, AR_TxRSSIAnt02);
+- ts->ts_rssi_ext0 = MS(ads->ds_txstatus5, AR_TxRSSIAnt10);
+- ts->ts_rssi_ext1 = MS(ads->ds_txstatus5, AR_TxRSSIAnt11);
+- ts->ts_rssi_ext2 = MS(ads->ds_txstatus5, AR_TxRSSIAnt12);
+ ts->evm0 = ads->AR_TxEVM0;
+ ts->evm1 = ads->AR_TxEVM1;
+ ts->evm2 = ads->AR_TxEVM2;
+- ts->ts_shortretry = MS(ads->ds_txstatus1, AR_RTSFailCnt);
+- ts->ts_longretry = MS(ads->ds_txstatus1, AR_DataFailCnt);
+- ts->ts_virtcol = MS(ads->ds_txstatus1, AR_VirtRetryCnt);
+- ts->tid = MS(ads->ds_txstatus9, AR_TxTid);
+- ts->ts_antenna = 0;
+
+ return 0;
+ }
+--- a/drivers/net/wireless/ath/ath9k/mac.h
++++ b/drivers/net/wireless/ath/ath9k/mac.h
+@@ -104,13 +104,11 @@ 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;
+@@ -121,7 +119,6 @@ struct ath_tx_status {
+ u8 qid;
+ u16 desc_id;
+ u8 tid;
+- u8 pad[2];
+ u32 ba_low;
+ u32 ba_high;
+ u32 evm0;
+--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+@@ -237,10 +237,12 @@ static int ar9003_hw_proc_txdesc(struct
+ struct ath_tx_status *ts)
+ {
+ struct ar9003_txs *ads;
++ u32 status;
+
+ ads = &ah->ts_ring[ah->ts_tail];
+
+- if ((ads->status8 & AR_TxDone) == 0)
++ status = ACCESS_ONCE(ads->status8);
++ if ((status & AR_TxDone) == 0)
+ return -EINPROGRESS;
+
+ ah->ts_tail = (ah->ts_tail + 1) % ah->ts_size;
+@@ -253,57 +255,58 @@ static int ar9003_hw_proc_txdesc(struct
+ return -EIO;
+ }
+
++ if (status & AR_TxOpExceeded)
++ ts->ts_status |= ATH9K_TXERR_XTXOP;
++ ts->ts_rateindex = MS(status, AR_FinalTxIdx);
++ ts->ts_seqnum = MS(status, AR_SeqNum);
++ ts->tid = MS(status, AR_TxTid);
++
+ ts->qid = MS(ads->ds_info, AR_TxQcuNum);
+ ts->desc_id = MS(ads->status1, AR_TxDescId);
+- ts->ts_seqnum = MS(ads->status8, AR_SeqNum);
+ ts->ts_tstamp = ads->status4;
+ ts->ts_status = 0;
+ ts->ts_flags = 0;
+
+- if (ads->status3 & AR_ExcessiveRetries)
++ status = ACCESS_ONCE(ads->status2);
++ ts->ts_rssi_ctl0 = MS(status, AR_TxRSSIAnt00);
++ ts->ts_rssi_ctl1 = MS(status, AR_TxRSSIAnt01);
++ ts->ts_rssi_ctl2 = MS(status, AR_TxRSSIAnt02);
++ if (status & AR_TxBaStatus) {
++ ts->ts_flags |= ATH9K_TX_BA;
++ ts->ba_low = ads->status5;
++ ts->ba_high = ads->status6;
++ }
++
++ status = ACCESS_ONCE(ads->status3);
++ if (status & AR_ExcessiveRetries)
+ ts->ts_status |= ATH9K_TXERR_XRETRY;
+- if (ads->status3 & AR_Filtered)
++ if (status & AR_Filtered)
+ ts->ts_status |= ATH9K_TXERR_FILT;
+- if (ads->status3 & AR_FIFOUnderrun) {
++ if (status & AR_FIFOUnderrun) {
+ ts->ts_status |= ATH9K_TXERR_FIFO;
+ ath9k_hw_updatetxtriglevel(ah, true);
+ }
+- if (ads->status8 & AR_TxOpExceeded)
+- ts->ts_status |= ATH9K_TXERR_XTXOP;
+- if (ads->status3 & AR_TxTimerExpired)
++ if (status & AR_TxTimerExpired)
+ ts->ts_status |= ATH9K_TXERR_TIMER_EXPIRED;
+-
+- if (ads->status3 & AR_DescCfgErr)
++ if (status & AR_DescCfgErr)
+ ts->ts_flags |= ATH9K_TX_DESC_CFG_ERR;
+- if (ads->status3 & AR_TxDataUnderrun) {
++ if (status & AR_TxDataUnderrun) {
+ ts->ts_flags |= ATH9K_TX_DATA_UNDERRUN;
+ ath9k_hw_updatetxtriglevel(ah, true);
+ }
+- if (ads->status3 & AR_TxDelimUnderrun) {
++ if (status & AR_TxDelimUnderrun) {
+ ts->ts_flags |= ATH9K_TX_DELIM_UNDERRUN;
+ ath9k_hw_updatetxtriglevel(ah, true);
+ }
+- if (ads->status2 & AR_TxBaStatus) {
+- ts->ts_flags |= ATH9K_TX_BA;
+- ts->ba_low = ads->status5;
+- ts->ba_high = ads->status6;
+- }
+-
+- ts->ts_rateindex = MS(ads->status8, AR_FinalTxIdx);
+-
+- ts->ts_rssi = MS(ads->status7, AR_TxRSSICombined);
+- ts->ts_rssi_ctl0 = MS(ads->status2, AR_TxRSSIAnt00);
+- ts->ts_rssi_ctl1 = MS(ads->status2, AR_TxRSSIAnt01);
+- ts->ts_rssi_ctl2 = MS(ads->status2, AR_TxRSSIAnt02);
+- ts->ts_rssi_ext0 = MS(ads->status7, AR_TxRSSIAnt10);
+- ts->ts_rssi_ext1 = MS(ads->status7, AR_TxRSSIAnt11);
+- ts->ts_rssi_ext2 = MS(ads->status7, AR_TxRSSIAnt12);
+- ts->ts_shortretry = MS(ads->status3, AR_RTSFailCnt);
+- ts->ts_longretry = MS(ads->status3, AR_DataFailCnt);
+- ts->ts_virtcol = MS(ads->status3, AR_VirtRetryCnt);
+- ts->ts_antenna = 0;
+-
+- ts->tid = MS(ads->status8, AR_TxTid);
++ ts->ts_shortretry = MS(status, AR_RTSFailCnt);
++ ts->ts_longretry = MS(status, AR_DataFailCnt);
++ ts->ts_virtcol = MS(status, AR_VirtRetryCnt);
++
++ status = ACCESS_ONCE(ads->status7);
++ ts->ts_rssi = MS(status, AR_TxRSSICombined);
++ ts->ts_rssi_ext0 = MS(status, AR_TxRSSIAnt10);
++ ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11);
++ ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12);
+
+ memset(ads, 0, sizeof(*ads));
+
diff --git a/package/mac80211/patches/553-ath9k_no_node_rssi.patch b/package/mac80211/patches/553-ath9k_no_node_rssi.patch
new file mode 100644
index 0000000000..5991485338
--- /dev/null
+++ b/package/mac80211/patches/553-ath9k_no_node_rssi.patch
@@ -0,0 +1,145 @@
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -271,7 +271,6 @@ struct ath_node {
+ struct ath_atx_ac ac[WME_NUM_AC];
+ u16 maxampdu;
+ u8 mpdudensity;
+- int last_rssi;
+ };
+
+ #define AGGR_CLEANUP BIT(1)
+@@ -666,6 +665,7 @@ struct ath_wiphy {
+ bool idle;
+ int chan_idx;
+ int chan_is_ht;
++ int last_rssi;
+ };
+
+ void ath9k_tasklet(unsigned long data);
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -553,7 +553,6 @@ static void ath_node_attach(struct ath_s
+ an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+ sta->ht_cap.ampdu_factor);
+ an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density);
+- an->last_rssi = ATH_RSSI_DUMMY_MARKER;
+ }
+ }
+
+@@ -822,9 +821,11 @@ static u32 ath_get_extchanmode(struct at
+ }
+
+ static void ath9k_bss_assoc_info(struct ath_softc *sc,
++ struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf)
+ {
++ struct ath_wiphy *aphy = hw->priv;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+
+@@ -848,6 +849,7 @@ static void ath9k_bss_assoc_info(struct
+ ath_beacon_config(sc, vif);
+
+ /* Reset rssi stats */
++ aphy->last_rssi = ATH_RSSI_DUMMY_MARKER;
+ sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
+
+ sc->sc_flags |= SC_OP_ANI_RUN;
+@@ -1969,7 +1971,7 @@ static void ath9k_bss_info_changed(struc
+ if (changed & BSS_CHANGED_ASSOC) {
+ ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
+ bss_conf->assoc);
+- ath9k_bss_assoc_info(sc, vif, bss_conf);
++ ath9k_bss_assoc_info(sc, hw, vif, bss_conf);
+ }
+
+ mutex_unlock(&sc->mutex);
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -960,36 +960,23 @@ static void ath9k_process_rssi(struct at
+ struct ieee80211_hdr *hdr,
+ struct ath_rx_status *rx_stats)
+ {
++ struct ath_wiphy *aphy = hw->priv;
+ struct ath_hw *ah = common->ah;
+- struct ieee80211_sta *sta;
+- struct ath_node *an;
+- int last_rssi = ATH_RSSI_DUMMY_MARKER;
++ int last_rssi;
+ __le16 fc;
+
+- fc = hdr->frame_control;
++ if (ah->opmode != NL80211_IFTYPE_STATION)
++ return;
+
+- rcu_read_lock();
+- /*
+- * XXX: use ieee80211_find_sta! This requires quite a bit of work
+- * under the current ath9k virtual wiphy implementation as we have
+- * no way of tying a vif to wiphy. Typically vifs are attached to
+- * at least one sdata of a wiphy on mac80211 but with ath9k virtual
+- * wiphy you'd have to iterate over every wiphy and each sdata.
+- */
+- if (is_multicast_ether_addr(hdr->addr1))
+- sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, NULL);
+- else
+- sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, hdr->addr1);
++ fc = hdr->frame_control;
++ if (!ieee80211_is_beacon(fc) ||
++ compare_ether_addr(hdr->addr3, common->curbssid))
++ return;
+
+- if (sta) {
+- an = (struct ath_node *) sta->drv_priv;
+- if (rx_stats->rs_rssi != ATH9K_RSSI_BAD &&
+- !rx_stats->rs_moreaggr)
+- ATH_RSSI_LPF(an->last_rssi, rx_stats->rs_rssi);
+- last_rssi = an->last_rssi;
+- }
+- rcu_read_unlock();
++ if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && !rx_stats->rs_moreaggr)
++ ATH_RSSI_LPF(aphy->last_rssi, rx_stats->rs_rssi);
+
++ last_rssi = aphy->last_rssi;
+ if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
+ rx_stats->rs_rssi = ATH_EP_RND(last_rssi,
+ ATH_RSSI_EP_MULTIPLIER);
+@@ -997,8 +984,7 @@ static void ath9k_process_rssi(struct at
+ rx_stats->rs_rssi = 0;
+
+ /* Update Beacon RSSI, this is used by ANI. */
+- if (ieee80211_is_beacon(fc))
+- ah->stats.avgbrssi = rx_stats->rs_rssi;
++ ah->stats.avgbrssi = rx_stats->rs_rssi;
+ }
+
+ /*
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -729,6 +729,7 @@ int ath9k_init_device(u16 devid, struct
+ const struct ath_bus_ops *bus_ops)
+ {
+ struct ieee80211_hw *hw = sc->hw;
++ struct ath_wiphy *aphy = hw->priv;
+ struct ath_common *common;
+ struct ath_hw *ah;
+ int error = 0;
+@@ -781,6 +782,7 @@ int ath9k_init_device(u16 devid, struct
+ 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);
++ aphy->last_rssi = ATH_RSSI_DUMMY_MARKER;
+
+ ath_init_leds(sc);
+ ath_start_rfkill_poll(sc);
+--- a/drivers/net/wireless/ath/ath9k/virtual.c
++++ b/drivers/net/wireless/ath/ath9k/virtual.c
+@@ -107,6 +107,7 @@ int ath9k_wiphy_add(struct ath_softc *sc
+ aphy->sc = sc;
+ aphy->hw = hw;
+ sc->sec_wiphy[i] = aphy;
++ aphy->last_rssi = ATH_RSSI_DUMMY_MARKER;
+ spin_unlock_bh(&sc->wiphy_lock);
+
+ memcpy(addr, common->macaddr, ETH_ALEN);
diff --git a/package/mac80211/patches/554-ath9k_bt_timer_start.patch b/package/mac80211/patches/554-ath9k_bt_timer_start.patch
new file mode 100644
index 0000000000..33aadb74f0
--- /dev/null
+++ b/package/mac80211/patches/554-ath9k_bt_timer_start.patch
@@ -0,0 +1,15 @@
+--- a/drivers/net/wireless/ath/ath9k/gpio.c
++++ b/drivers/net/wireless/ath/ath9k/gpio.c
+@@ -326,10 +326,8 @@ static void ath_btcoex_period_timer(unsi
+
+ timer_period = is_btscan ? btcoex->btscan_no_stomp :
+ btcoex->btcoex_no_stomp;
+- ath9k_gen_timer_start(ah,
+- btcoex->no_stomp_timer,
+- (ath9k_hw_gettsf32(ah) +
+- timer_period), timer_period * 10);
++ ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, 0,
++ timer_period * 10);
+ btcoex->hw_timer_enabled = true;
+ }
+
diff --git a/package/mac80211/patches/555-ath9k_hw_gettsf32_static.patch b/package/mac80211/patches/555-ath9k_hw_gettsf32_static.patch
new file mode 100644
index 0000000000..6fc4bc7210
--- /dev/null
+++ b/package/mac80211/patches/555-ath9k_hw_gettsf32_static.patch
@@ -0,0 +1,25 @@
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -2324,11 +2324,10 @@ static u32 rightmost_index(struct ath_ge
+ return timer_table->gen_timer_index[b];
+ }
+
+-u32 ath9k_hw_gettsf32(struct ath_hw *ah)
++static u32 ath9k_hw_gettsf32(struct ath_hw *ah)
+ {
+ return REG_READ(ah, AR_TSF_L32);
+ }
+-EXPORT_SYMBOL(ath9k_hw_gettsf32);
+
+ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
+ void (*trigger)(void *),
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -892,7 +892,6 @@ void ath9k_hw_gen_timer_stop(struct ath_
+
+ void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer);
+ void ath_gen_timer_isr(struct ath_hw *hw);
+-u32 ath9k_hw_gettsf32(struct ath_hw *ah);
+
+ void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len);
+
diff --git a/package/mac80211/patches/556-ath9k_desc_alignment.patch b/package/mac80211/patches/556-ath9k_desc_alignment.patch
new file mode 100644
index 0000000000..6295f5eed2
--- /dev/null
+++ b/package/mac80211/patches/556-ath9k_desc_alignment.patch
@@ -0,0 +1,49 @@
+--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.h
++++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.h
+@@ -65,7 +65,7 @@ struct ar9003_rxs {
+ u32 status9;
+ u32 status10;
+ u32 status11;
+-} __packed;
++} __packed __aligned(4);
+
+ /* Transmit Control Descriptor */
+ struct ar9003_txc {
+@@ -93,7 +93,7 @@ struct ar9003_txc {
+ u32 ctl21; /* DMA control 21 */
+ u32 ctl22; /* DMA control 22 */
+ u32 pad[9]; /* pad to cache line (128 bytes/32 dwords) */
+-} __packed;
++} __packed __aligned(4);
+
+ struct ar9003_txs {
+ u32 ds_info;
+@@ -105,7 +105,7 @@ struct ar9003_txs {
+ u32 status6;
+ u32 status7;
+ u32 status8;
+-} __packed;
++} __packed __aligned(4);
+
+ void ar9003_hw_attach_mac_ops(struct ath_hw *hw);
+ void ath9k_hw_set_rx_bufsize(struct ath_hw *ah, u16 buf_size);
+--- a/drivers/net/wireless/ath/ath9k/mac.h
++++ b/drivers/net/wireless/ath/ath9k/mac.h
+@@ -237,7 +237,7 @@ struct ath_desc {
+ u32 ds_ctl1;
+ u32 ds_hw[20];
+ void *ds_vdata;
+-} __packed;
++} __packed __aligned(4);
+
+ #define ATH9K_TXDESC_CLRDMASK 0x0001
+ #define ATH9K_TXDESC_NOACK 0x0002
+@@ -307,7 +307,7 @@ struct ar5416_desc {
+ u32 status8;
+ } rx;
+ } u;
+-} __packed;
++} __packed __aligned(4);
+
+ #define AR5416DESC(_ds) ((struct ar5416_desc *)(_ds))
+ #define AR5416DESC_CONST(_ds) ((const struct ar5416_desc *)(_ds))