ath9k: add some rx path fixes
[openwrt/staging/lynxis.git] / package / kernel / mac80211 / patches / 300-pending_work.patch
index 5c7f7e0019f80ca73deab53a1c6ffbedd34d1ad6..ef81721529eee36aa5faa2a55a14cd6d887d177f 100644 (file)
@@ -1,3 +1,164 @@
+commit 230ab8c1880266c9cfceac962e2d48309dea79a7
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Mon May 19 21:48:56 2014 +0200
+
+    ath9k: re-schedule rx processing after budget exceeded
+    
+    Should improve rx stability under load
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit 27647baeaee1b12bc3c57ccf1c7eba53bcd7fe53
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Mon May 19 21:20:49 2014 +0200
+
+    ath9k: avoid passing buffers to the hardware during flush
+    
+    The commit "ath9k: fix possible hang on flush" changed the receive code
+    to always link rx descriptors of processed frames, even when flushing.
+    In some cases, this leads to flushed rx buffers being passed to the
+    hardware while rx is already stopped.
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit 92e9dd662542683856e62a5e7e43fcf5b9da5c4a
+Author: Henning Rogge <hrogge@gmail.com>
+Date:   Thu May 1 10:03:46 2014 +0200
+
+    mac80211: Fix mac80211 station info rx bitrate for IBSS mode
+    
+    Filter out incoming multicast packages before applying their bitrate
+    to the rx bitrate station info field to prevent them from setting the
+    rx bitrate to the basic multicast rate.
+    
+    Signed-off-by: Henning Rogge <hrogge@gmail.com>
+
+commit 4c8a3486cb577d40c1ef75f0a8dc9a04773eef83
+Author: Nickolay Ledovskikh <nledovskikh@gmail.com>
+Date:   Fri Apr 25 22:53:34 2014 +0400
+
+    ath5k: Fix AR5K_PHY_TXPOWER_RATE_MAX register value setting.
+    
+    I was reading ath5k power setting code and
+    noticed typing error in ath5k_hw_txpower function.
+    Invalid value was written to AR5K_PHY_TXPOWER_RATE_MAX
+    register.
+    
+    Signed-off-by: Nikolay Ledovskikh <nledovskikh@gmail.com>
+    Acked-by: Nick Kossifidis <mickflemm@gmail.com>
+    Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+commit 4d76248013dbb1948429555208900a585b0f351d
+Author: Janusz Dziedzic <janusz.dziedzic@tieto.com>
+Date:   Tue Apr 8 13:38:43 2014 +0200
+
+    ath9k: Enable DFS only when ATH9K_DFS_CERTIFIED
+    
+    Add DFS interface combination only when
+    CONFIG_ATH9K_DFS_CERTIFIED is set. In other case
+    user can run CAC/beaconing without proper handling
+    of pulse events (without radar detection activated).
+    
+    Reported-by: Cedric Voncken <cedric.voncken@acksys.fr>
+    Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
+    Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+commit c83a4e5156a4b4dd22137d33a5625440982d6d37
+Author: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
+Date:   Mon Apr 28 21:17:08 2014 +0530
+
+    ath9k_hw: fix worse EVM for 11b rates
+    
+    Adjust FIR filter co-efficients to improve EVM for 11b rates.
+    
+    Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
+    Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+commit 8aab2c7a2f4a957e344db429dfb1190ae59ce8b5
+Author: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
+Date:   Mon Apr 28 21:17:07 2014 +0530
+
+    ath9k_hw: update ar9300 initvals
+    
+     * rfsat gainchange hysteresis of rf_gain stuck with large
+       interference present.
+    
+    Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
+    Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+commit 8c7ae357cc5b6bd037ad2d666e9f3789cf882925
+Author: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
+Date:   Wed Apr 23 15:07:57 2014 +0530
+
+    ath9k: fix race in setting ATH_OP_INVALID
+    
+    The commit "ath9k: move sc_flags to ath_common" moved setting
+    ATH_OP_INVALID flag below ieee80211_register_hw. This is causing
+    the flag never being cleared randomly as the drv_start is called
+    prior to setting flag. Fix this by setting the flag prior to
+    register_hw.
+    
+    Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
+    Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+commit c82552c5b0cb1735dbcbad78b1ffc6d3c212dc56
+Author: Tim Harvey <tharvey@gateworks.com>
+Date:   Mon Apr 21 16:14:57 2014 -0700
+
+    ath9k: add a recv budget
+    
+    Implement a recv budget so that in cases of high traffic we still allow other
+    taskets to get processed.
+    
+    Without this, we can encounter a host of issues during high wireless traffic
+    reception depending on system load including rcu stall's detected (ARM),
+    soft lockups, failure to service critical tasks such as watchdog resets,
+    and triggering of the tx stuck tasklet.
+    
+    The same thing was proposed previously by Ben:
+     http://www.spinics.net/lists/linux-wireless/msg112891.html
+    
+    The only difference here is that I make sure only processed packets are counted
+    in the budget by checking at the end of the rx loop.
+    
+    Signed-off-by: Tim Harvey <tharvey@gateworks.com>
+    Acked-by: Felix Fietkau <nbd@openwrt.org>
+    Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+commit 3a758134e66ca74a9df792616b5288b2fa2cfd7f
+Author: Tim Harvey <tharvey@gateworks.com>
+Date:   Mon Apr 21 16:14:56 2014 -0700
+
+    ath9k: fix possible hang on flush
+    
+    If a flush is requested, make sure to clear the descriptor once we've
+    processed it.
+    
+    This resolves a hang that will occur if all RX descriptors are full when a
+    flush is requested.
+    
+    Signed-off-by: Tim Harvey <tharvey@gateworks.com>
+    Acked-by: Felix Fietkau <nbd@openwrt.org>
+    Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+commit eefb1d6adc4c60d219182b8917e4567484ce07fc
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Mon Apr 28 18:27:41 2014 +0200
+
+    ath9k: remove tid->paused flag
+    
+    There are some corner cases where the driver could get stuck with a full
+    tid queue that is paused, leading to a software tx queue hang.
+    
+    Since the tx queueing rework, pausing per-tid queues on aggregation
+    session setup is no longer necessary. The driver will assign sequence
+    numbers to buffered frames when a new session is established, in order
+    to get the correct starting sequence number.
+    
+    mac80211 prevents new frames from entering the queue during setup.
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
 commit 98a713933d8495f4078f561c1e651b738dd5b531
 Author: Felix Fietkau <nbd@openwrt.org>
 Date:   Sun Apr 27 14:49:03 2014 +0200
@@ -137,3 +298,438 @@ Date:   Sun Apr 6 23:35:28 2014 +0200
        if (ah->opmode == NL80211_IFTYPE_STATION &&
            BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_LOW &&
            immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI)
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -251,7 +251,6 @@ struct ath_atx_tid {
+       s8 bar_index;
+       bool sched;
+-      bool paused;
+       bool active;
+ };
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -107,9 +107,6 @@ static void ath_tx_queue_tid(struct ath_
+ {
+       struct ath_atx_ac *ac = tid->ac;
+-      if (tid->paused)
+-              return;
+-
+       if (tid->sched)
+               return;
+@@ -1407,7 +1404,6 @@ int ath_tx_aggr_start(struct ath_softc *
+       ath_tx_tid_change_state(sc, txtid);
+       txtid->active = true;
+-      txtid->paused = true;
+       *ssn = txtid->seq_start = txtid->seq_next;
+       txtid->bar_index = -1;
+@@ -1427,7 +1423,6 @@ void ath_tx_aggr_stop(struct ath_softc *
+       ath_txq_lock(sc, txq);
+       txtid->active = false;
+-      txtid->paused = false;
+       ath_tx_flush_tid(sc, txtid);
+       ath_tx_tid_change_state(sc, txtid);
+       ath_txq_unlock_complete(sc, txq);
+@@ -1487,7 +1482,7 @@ void ath_tx_aggr_wakeup(struct ath_softc
+               ath_txq_lock(sc, txq);
+               ac->clear_ps_filter = true;
+-              if (!tid->paused && ath_tid_has_buffered(tid)) {
++              if (ath_tid_has_buffered(tid)) {
+                       ath_tx_queue_tid(txq, tid);
+                       ath_txq_schedule(sc, txq);
+               }
+@@ -1510,7 +1505,6 @@ void ath_tx_aggr_resume(struct ath_softc
+       ath_txq_lock(sc, txq);
+       tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
+-      tid->paused = false;
+       if (ath_tid_has_buffered(tid)) {
+               ath_tx_queue_tid(txq, tid);
+@@ -1544,8 +1538,6 @@ void ath9k_release_buffered_frames(struc
+                       continue;
+               tid = ATH_AN_2_TID(an, i);
+-              if (tid->paused)
+-                      continue;
+               ath_txq_lock(sc, tid->ac->txq);
+               while (nframes > 0) {
+@@ -1844,9 +1836,6 @@ void ath_txq_schedule(struct ath_softc *
+                       list_del(&tid->list);
+                       tid->sched = false;
+-                      if (tid->paused)
+-                              continue;
+-
+                       if (ath_tx_sched_aggr(sc, txq, tid, &stop))
+                               sent = true;
+@@ -2698,7 +2687,6 @@ void ath_tx_node_init(struct ath_softc *
+               tid->baw_size  = WME_MAX_BA;
+               tid->baw_head  = tid->baw_tail = 0;
+               tid->sched     = false;
+-              tid->paused    = false;
+               tid->active        = false;
+               __skb_queue_head_init(&tid->buf_q);
+               __skb_queue_head_init(&tid->retry_q);
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -34,7 +34,8 @@ static inline bool ath9k_check_auto_slee
+  * buffer (or rx fifo). This can incorrectly acknowledge packets
+  * to a sender if last desc is self-linked.
+  */
+-static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf)
++static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf,
++                          bool flush)
+ {
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+@@ -59,18 +60,19 @@ static void ath_rx_buf_link(struct ath_s
+                            common->rx_bufsize,
+                            0);
+-      if (sc->rx.rxlink == NULL)
+-              ath9k_hw_putrxbuf(ah, bf->bf_daddr);
+-      else
++      if (sc->rx.rxlink)
+               *sc->rx.rxlink = bf->bf_daddr;
++      else if (!flush)
++              ath9k_hw_putrxbuf(ah, bf->bf_daddr);
+       sc->rx.rxlink = &ds->ds_link;
+ }
+-static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf)
++static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf,
++                            bool flush)
+ {
+       if (sc->rx.buf_hold)
+-              ath_rx_buf_link(sc, sc->rx.buf_hold);
++              ath_rx_buf_link(sc, sc->rx.buf_hold, flush);
+       sc->rx.buf_hold = bf;
+ }
+@@ -106,7 +108,7 @@ static void ath_opmode_init(struct ath_s
+ }
+ static bool ath_rx_edma_buf_link(struct ath_softc *sc,
+-                               enum ath9k_rx_qtype qtype)
++                               enum ath9k_rx_qtype qtype, bool flush)
+ {
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_rx_edma *rx_edma;
+@@ -127,7 +129,8 @@ static bool ath_rx_edma_buf_link(struct 
+                               ah->caps.rx_status_len, DMA_TO_DEVICE);
+       SKB_CB_ATHBUF(skb) = bf;
+-      ath9k_hw_addrxbuf_edma(ah, bf->bf_buf_addr, qtype);
++      if (!flush)
++              ath9k_hw_addrxbuf_edma(ah, bf->bf_buf_addr, qtype);
+       __skb_queue_tail(&rx_edma->rx_fifo, skb);
+       return true;
+@@ -145,7 +148,7 @@ static void ath_rx_addbuffer_edma(struct
+       }
+       list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list)
+-              if (!ath_rx_edma_buf_link(sc, qtype))
++              if (!ath_rx_edma_buf_link(sc, qtype, false))
+                       break;
+ }
+@@ -442,7 +445,7 @@ int ath_startrecv(struct ath_softc *sc)
+       sc->rx.buf_hold = NULL;
+       sc->rx.rxlink = NULL;
+       list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
+-              ath_rx_buf_link(sc, bf);
++              ath_rx_buf_link(sc, bf, false);
+       }
+       /* We could have deleted elements so the list may be empty now */
+@@ -636,7 +639,7 @@ static bool ath_edma_get_buffers(struct 
+       if (ret == -EINVAL) {
+               /* corrupt descriptor, skip this one and the following one */
+               list_add_tail(&bf->list, &sc->rx.rxbuf);
+-              ath_rx_edma_buf_link(sc, qtype);
++              ath_rx_edma_buf_link(sc, qtype, false);
+               skb = skb_peek(&rx_edma->rx_fifo);
+               if (skb) {
+@@ -645,7 +648,7 @@ static bool ath_edma_get_buffers(struct 
+                       __skb_unlink(skb, &rx_edma->rx_fifo);
+                       list_add_tail(&bf->list, &sc->rx.rxbuf);
+-                      ath_rx_edma_buf_link(sc, qtype);
++                      ath_rx_edma_buf_link(sc, qtype, false);
+               }
+               bf = NULL;
+@@ -975,6 +978,7 @@ int ath_rx_tasklet(struct ath_softc *sc,
+       u64 tsf = 0;
+       unsigned long flags;
+       dma_addr_t new_buf_addr;
++      unsigned int budget = 512;
+       if (edma)
+               dma_type = DMA_BIDIRECTIONAL;
+@@ -1113,15 +1117,17 @@ requeue_drop_frag:
+               }
+ requeue:
+               list_add_tail(&bf->list, &sc->rx.rxbuf);
+-              if (flush)
+-                      continue;
+               if (edma) {
+-                      ath_rx_edma_buf_link(sc, qtype);
++                      ath_rx_edma_buf_link(sc, qtype, flush);
+               } else {
+-                      ath_rx_buf_relink(sc, bf);
+-                      ath9k_hw_rxena(ah);
++                      ath_rx_buf_relink(sc, bf, flush);
++                      if (!flush)
++                              ath9k_hw_rxena(ah);
+               }
++
++              if (!budget--)
++                      break;
+       } while (1);
+       if (!(ah->imask & ATH9K_INT_RXEOL)) {
+@@ -1129,5 +1135,5 @@ requeue:
+               ath9k_hw_set_interrupts(ah);
+       }
+-      return 0;
++      return !budget;
+ }
+--- a/drivers/net/wireless/ath/ath9k/ahb.c
++++ b/drivers/net/wireless/ath/ath9k/ahb.c
+@@ -86,7 +86,6 @@ static int ath_ahb_probe(struct platform
+       int irq;
+       int ret = 0;
+       struct ath_hw *ah;
+-      struct ath_common *common;
+       char hw_name[64];
+       if (!dev_get_platdata(&pdev->dev)) {
+@@ -146,9 +145,6 @@ static int ath_ahb_probe(struct platform
+       wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n",
+                  hw_name, (unsigned long)mem, irq);
+-      common = ath9k_hw_common(sc->sc_ah);
+-      /* Will be cleared in ath9k_start() */
+-      set_bit(ATH_OP_INVALID, &common->op_flags);
+       return 0;
+  err_irq:
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -670,6 +670,7 @@ static const struct ieee80211_iface_comb
+               .num_different_channels = 1,
+               .beacon_int_infra_match = true,
+       },
++#ifdef CONFIG_ATH9K_DFS_CERTIFIED
+       {
+               .limits = if_dfs_limits,
+               .n_limits = ARRAY_SIZE(if_dfs_limits),
+@@ -679,6 +680,7 @@ static const struct ieee80211_iface_comb
+               .radar_detect_widths =  BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                       BIT(NL80211_CHAN_WIDTH_20),
+       }
++#endif
+ };
+ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+@@ -781,6 +783,9 @@ int ath9k_init_device(u16 devid, struct 
+       common = ath9k_hw_common(ah);
+       ath9k_set_hw_capab(sc, hw);
++      /* Will be cleared in ath9k_start() */
++      set_bit(ATH_OP_INVALID, &common->op_flags);
++
+       /* Initialize regulatory */
+       error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
+                             ath9k_reg_notifier);
+--- a/drivers/net/wireless/ath/ath9k/pci.c
++++ b/drivers/net/wireless/ath/ath9k/pci.c
+@@ -784,7 +784,6 @@ static int ath_pci_probe(struct pci_dev 
+ {
+       struct ath_softc *sc;
+       struct ieee80211_hw *hw;
+-      struct ath_common *common;
+       u8 csz;
+       u32 val;
+       int ret = 0;
+@@ -877,10 +876,6 @@ static int ath_pci_probe(struct pci_dev 
+       wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n",
+                  hw_name, (unsigned long)sc->mem, pdev->irq);
+-      /* Will be cleared in ath9k_start() */
+-      common = ath9k_hw_common(sc->sc_ah);
+-      set_bit(ATH_OP_INVALID, &common->op_flags);
+-
+       return 0;
+ err_init:
+--- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
++++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
+@@ -410,7 +410,7 @@ static const u32 ar9300_2p2_baseband_cor
+       {0x00009e30, 0x06336f77},
+       {0x00009e34, 0x6af6532f},
+       {0x00009e38, 0x0cc80c00},
+-      {0x00009e40, 0x0d261820},
++      {0x00009e40, 0x0d261800},
+       {0x00009e4c, 0x00001004},
+       {0x00009e50, 0x00ff03f1},
+       {0x00009e54, 0x00000000},
+--- a/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h
++++ b/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h
+@@ -592,7 +592,7 @@ static const u32 ar9331_1p1_baseband_cor
+       {0x00009e30, 0x06336f77},
+       {0x00009e34, 0x6af6532f},
+       {0x00009e38, 0x0cc80c00},
+-      {0x00009e40, 0x0d261820},
++      {0x00009e40, 0x0d261800},
+       {0x00009e4c, 0x00001004},
+       {0x00009e50, 0x00ff03f1},
+       {0x00009fc0, 0x803e4788},
+--- a/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h
++++ b/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h
+@@ -231,7 +231,7 @@ static const u32 ar9331_1p2_baseband_cor
+       {0x00009e30, 0x06336f77},
+       {0x00009e34, 0x6af6532f},
+       {0x00009e38, 0x0cc80c00},
+-      {0x00009e40, 0x0d261820},
++      {0x00009e40, 0x0d261800},
+       {0x00009e4c, 0x00001004},
+       {0x00009e50, 0x00ff03f1},
+       {0x00009fc0, 0x803e4788},
+--- a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h
++++ b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h
+@@ -318,7 +318,7 @@ static const u32 ar9340_1p0_baseband_cor
+       {0x00009e30, 0x06336f77},
+       {0x00009e34, 0x6af6532f},
+       {0x00009e38, 0x0cc80c00},
+-      {0x00009e40, 0x0d261820},
++      {0x00009e40, 0x0d261800},
+       {0x00009e4c, 0x00001004},
+       {0x00009e50, 0x00ff03f1},
+       {0x00009e54, 0x00000000},
+@@ -348,9 +348,9 @@ static const u32 ar9340_1p0_baseband_cor
+       {0x0000a370, 0x00000000},
+       {0x0000a390, 0x00000001},
+       {0x0000a394, 0x00000444},
+-      {0x0000a398, 0x00000000},
+-      {0x0000a39c, 0x210d0401},
+-      {0x0000a3a0, 0xab9a7144},
++      {0x0000a398, 0x001f0e0f},
++      {0x0000a39c, 0x0075393f},
++      {0x0000a3a0, 0xb79f6427},
+       {0x0000a3a4, 0x00000000},
+       {0x0000a3a8, 0xaaaaaaaa},
+       {0x0000a3ac, 0x3c466478},
+--- a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
++++ b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
+@@ -90,7 +90,7 @@ static const u32 ar9580_1p0_baseband_cor
+       {0x00009e30, 0x06336f77},
+       {0x00009e34, 0x6af6532f},
+       {0x00009e38, 0x0cc80c00},
+-      {0x00009e40, 0x0d261820},
++      {0x00009e40, 0x0d261800},
+       {0x00009e4c, 0x00001004},
+       {0x00009e50, 0x00ff03f1},
+       {0x00009e54, 0x00000000},
+--- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
++++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
+@@ -257,9 +257,9 @@ static const u32 qca953x_1p0_baseband_co
+       {0x0000a370, 0x00000000},
+       {0x0000a390, 0x00000001},
+       {0x0000a394, 0x00000444},
+-      {0x0000a398, 0x1f020503},
+-      {0x0000a39c, 0x29180c03},
+-      {0x0000a3a0, 0x9a8b6844},
++      {0x0000a398, 0x001f0e0f},
++      {0x0000a39c, 0x0075393f},
++      {0x0000a3a0, 0xb79f6427},
+       {0x0000a3a4, 0x000000ff},
+       {0x0000a3a8, 0x6a6a6a6a},
+       {0x0000a3ac, 0x6a6a6a6a},
+--- a/drivers/net/wireless/ath/ath5k/phy.c
++++ b/drivers/net/wireless/ath/ath5k/phy.c
+@@ -3709,8 +3709,8 @@ ath5k_hw_txpower(struct ath5k_hw *ah, st
+                       AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
+                       AR5K_TPC);
+       } else {
+-              ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX |
+-                      AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
++              ath5k_hw_reg_write(ah, AR5K_TUNE_MAX_TXPOWER,
++                      AR5K_PHY_TXPOWER_RATE_MAX);
+       }
+       return 0;
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -1231,7 +1231,8 @@ ieee80211_rx_h_sta_process(struct ieee80
+               if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid) &&
+                   test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
+                       sta->last_rx = jiffies;
+-                      if (ieee80211_is_data(hdr->frame_control)) {
++                      if (ieee80211_is_data(hdr->frame_control) &&
++                          !is_multicast_ether_addr(hdr->addr1)) {
+                               sta->last_rx_rate_idx = status->rate_idx;
+                               sta->last_rx_rate_flag = status->flag;
+                               sta->last_rx_rate_vht_flag = status->vht_flag;
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -442,6 +442,8 @@ void ath9k_tasklet(unsigned long data)
+       ath9k_ps_wakeup(sc);
+       spin_lock(&sc->sc_pcu_lock);
++      sc->intrstatus = 0;
++
+       if (status & ATH9K_INT_FATAL) {
+               type = RESET_TYPE_FATAL_INT;
+               ath9k_queue_reset(sc, type);
+@@ -510,10 +512,12 @@ void ath9k_tasklet(unsigned long data)
+       if (status & rxmask) {
+               /* Check for high priority Rx first */
+               if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
+-                  (status & ATH9K_INT_RXHP))
+-                      ath_rx_tasklet(sc, 0, true);
++                  (status & ATH9K_INT_RXHP) &&
++                  ath_rx_tasklet(sc, 0, true))
++                      sc->intrstatus |= ATH9K_INT_RXHP;
+-              ath_rx_tasklet(sc, 0, false);
++              if (ath_rx_tasklet(sc, 0, false))
++                      sc->intrstatus |= ATH9K_INT_RXLP;
+       }
+       if (status & ATH9K_INT_TX) {
+@@ -541,6 +545,9 @@ void ath9k_tasklet(unsigned long data)
+       /* re-enable hardware interrupt */
+       ath9k_hw_enable_interrupts(ah);
++      if (sc->intrstatus)
++              tasklet_schedule(&sc->intr_tq);
++
+ out:
+       spin_unlock(&sc->sc_pcu_lock);
+       ath9k_ps_restore(sc);
+@@ -607,7 +614,7 @@ irqreturn_t ath_isr(int irq, void *dev)
+               return IRQ_NONE;
+       /* Cache the status */
+-      sc->intrstatus = status;
++      sc->intrstatus |= status;
+       if (status & SCHED_INTR)
+               sched = true;