ath9k: further improvements to noise immunity handling on older chips
[openwrt.git] / package / kernel / mac80211 / patches / 300-pending_work.patch
index fc428cf..897f6c4 100644 (file)
@@ -1,3 +1,249 @@
+commit 3a0f984b1cdcd6a9f8c441635ef3b05d58547f4e
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Tue Mar 11 14:03:32 2014 +0100
+
+    ath9k_hw: set ANI firstep as absolute values instead of relative
+    
+    On older chips, the INI value differ in similar ways as cycpwr_thr1, so
+    convert it to absolute values as well.
+    
+    Since the ANI algorithm is different here compared to the old
+    implementation (fewer steps, controlled at a different point in time),
+    it makes sense to use values similar to what would be applied for newer
+    chips, just without relying on INI defaults.
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit 91d70d40400c569b49605b78fd7c43e9405694f4
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Tue Mar 11 14:00:37 2014 +0100
+
+    ath9k_hw: set ANI cycpwr_thr1 as absolute values instead of relative
+    
+    The table was copied from the ANI implementation of AR9300. It assumes
+    that the INI values contain a baseline value that is usable as reference
+    from which to increase/decrease based on the noise immunity value.
+    
+    On older chips, the differences are bigger and especially AR5008/AR9001
+    are configured to much more sensitive values than what is useful.
+    
+    Improve ANI behavior by reverting to the absolute values used in the
+    previous implementation (expressed as a simple formula instead of the
+    old table).
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit c977493766310a825f406836636ffd66e1447783
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Mon Mar 10 19:52:56 2014 +0100
+
+    ath9k_hw: remove ANI function restrictions for AP mode
+    
+    The primary purpose of this piece of code was to selectively disable
+    OFDM weak signal detection. The checks for this are elsewhere, and an
+    earlier commit relaxed the restrictions for older chips, which are more
+    sensitive to interference.
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit 8d804f1af11e4e058b1e8453327777d73a585cb8
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Sun Mar 9 11:25:43 2014 +0100
+
+    ath9k: clean up and enhance ANI debugfs file
+    
+    Unify scnprintf calls and include the current OFDM/CCK immunity level.
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit 22e298b5a3a8a49e33805d4e351965123dede35b
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Sun Mar 9 10:58:47 2014 +0100
+
+    ath9k: fix ready time of the multicast buffer queue
+    
+    qi->tqi_readyTime is written directly to registers that expect
+    microseconds as unit instead of TU.
+    When setting the CABQ ready time, cur_conf->beacon_interval is in TU, so
+    convert it to microseconds before passing it to ath9k_hw.
+    
+    This should hopefully fix some Tx DMA issues with buffered multicast
+    frames in AP mode.
+    
+    Cc: stable@vger.kernel.org
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit fcb064fdd5a27bec8d24099bc0172468f34c97cb
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Sun Mar 9 09:43:09 2014 +0100
+
+    ath9k_hw: fix unreachable code in baseband hang detection code
+    
+    The commit "ath9k: reduce baseband hang detection false positive rate"
+    added a delay in the loop checking the baseband state, however it was
+    unreachable due to previous 'continue' statements.
+    
+    Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit 31959d8df39319e32c6d5ba9c135727be90cfad7
+Author: Michal Kazior <michal.kazior@tieto.com>
+Date:   Fri Mar 7 08:09:38 2014 +0100
+
+    mac80211: fix possible NULL dereference
+    
+    If chanctx is missing on a given vif then the band
+    is assumed to be 2GHz. However if hw doesn't
+    support 2GHz band then mac80211 ended up with a
+    NULL dereference.
+    
+    This fixes a splat:
+    
+    [ 4605.207223] BUG: unable to handle kernel NULL pointer dereference at 0000000000000018
+    [ 4605.210789] IP: [<ffffffffa07b5635>] ieee80211_parse_bitrates+0x65/0x110 [mac80211]
+    
+    The splat was preceeded by WARN_ON(!chanctx_conf)
+    in ieee80211_get_sdata_band().
+    
+    Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
+
+commit 6c5a3ffa0a2d22c091a2717f427259bacf77ac5e
+Author: Michael Braun <michael-dev@fami-braun.de>
+Date:   Thu Mar 6 15:08:43 2014 +0100
+
+    mac80211: fix WPA with VLAN on AP side with ps-sta again
+    
+    commit de74a1d9032f4d37ea453ad2a647e1aff4cd2591
+      "mac80211: fix WPA with VLAN on AP side with ps-sta"
+    fixed an issue where queued multicast packets would
+    be sent out encrypted with the key of an other bss.
+    
+    commit "7cbf9d017dbb5e3276de7d527925d42d4c11e732"
+      "mac80211: fix oops on mesh PS broadcast forwarding"
+    essentially reverted it, because vif.type cannot be AP_VLAN
+    due to the check to vif.type in ieee80211_get_buffered_bc before.
+    
+    As the later commit intended to fix the MESH case, fix it
+    by checking for IFTYPE_AP instead of IFTYPE_AP_VLAN.
+    
+    Fixes: 7cbf9d017dbb
+    Cc: <stable@vger.kernel.org> # 3.10.x
+    Cc: <stable@vger.kernel.org> # 3.11.x
+    Cc: <stable@vger.kernel.org> # 3.12.x
+    Cc: <stable@vger.kernel.org> # 3.13.x
+    Cc: <linux-wireless@vger.kernel.org>
+    Cc: <projekt-wlan@fem.tu-ilmenau.de>
+    Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
+
+commit 9d6ab9bdb9b368a6cf9519f0f92509b5b2c297ec
+Author: Johannes Berg <johannes.berg@intel.com>
+Date:   Mon Mar 3 14:19:08 2014 +0100
+
+    cfg80211: remove racy beacon_interval assignment
+    
+    In case of AP mode, the beacon interval is already reset to
+    zero inside cfg80211_stop_ap(), and in the other modes it
+    isn't relevant. Remove the assignment to remove a potential
+    race since the assignment isn't properly locked.
+    
+    Reported-by: Michal Kazior <michal.kazior@tieto.com>
+    Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+
+commit 1abdeca3c6fb9cf1f84f85e78ed8d1c33bd69db0
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Fri Feb 28 18:52:56 2014 +0100
+
+    ath9k_hw: tweak noise immunity thresholds for older chipsets
+    
+    Older chipsets are more sensitive to high PHY error counts, and the
+    current noise immunity thresholds were based on tests run at QCA with
+    newer chipsets.
+    
+    This patch brings back the values from the old ANI implementation for
+    old chipsets, and it also disables weak signal detection on an earlier
+    noise immunity level, to improve overall radio stability on affected
+    devices.
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit 431e506da5953adc3b65af25f4b90873d528c115
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Fri Feb 28 18:44:13 2014 +0100
+
+    ath9k_hw: toggle weak signal detection in AP mode on older chipsets
+    
+    The commit 80b4205b "ath9k: Fix OFDM weak signal detection for AP mode"
+    prevented weak signal detection changes from taking effect in AP mode on
+    all chipsets, claiming it is "not allowed".
+    
+    The main reason for not disabling weak signal detection in AP mode is
+    that typically beacon RSSI is used to track whether it is needed to
+    boost range, and this is unavailable in AP mode for obvious reasons.
+    
+    The problem with not disabling weak signal detection is that older
+    chipsets are very sensitive to high PHY error counts. When faced with
+    heavy noise, this can lead to an excessive amount of "Failed to stop
+    TX DMA" errors in the field.
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit 98d1a6c5b14688ed030e81b889f607be308e0df9
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Mon Feb 24 22:20:32 2014 +0100
+
+    ath9k: fix invalid descriptor discarding
+    
+    Only set sc->rx.discard_next to rx_stats->rs_more when actually
+    discarding the current descriptor.
+    
+    Also, fix a detection of broken descriptors:
+    First the code checks if the current descriptor is not done.
+    Then it checks if the next descriptor is done.
+    Add a check that afterwards checks the first descriptor again, because
+    it might have been completed in the mean time.
+    
+    This fixes a regression introduced in
+    commit 723e711356b5a8a95728a890e254e8b0d47b55cf
+    "ath9k: fix handling of broken descriptors"
+    
+    Cc: stable@vger.kernel.org
+    Reported-by: Marco AndrĂ© Dinis <marcoandredinis@gmail.com>
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit 52a46300e782fe6994466523eb2b0b59091ea59f
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Mon Feb 24 11:43:50 2014 +0100
+
+    ath9k: reduce baseband hang detection false positive rate
+    
+    Check if the baseband state remains stable, and add a small delay
+    between register reads.
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit 118945bb12082e9d4edddc868d88143164e0f440
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Sat Feb 22 14:55:23 2014 +0100
+
+    ath5k: set SURVEY_INFO_IN_USE on get_survey
+    
+    Only one channel is returned - the one currently being used.
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+commit ee41f72476e1ea44283dfe1cbf75b9543a1e15c8
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Sat Feb 22 14:44:52 2014 +0100
+
+    ath9k: make some hardware reset log messages debug-only
+    
+    On some chips, baseband watchdog hangs are more common than others, and
+    the driver has support for handling them.
+    Interrupts even after a watchdog hang are also quite common, so there's
+    not much point in spamming the user's logfiles.
+    
+    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
 commit b14fbb554fc65a2e0b5c41a319269b0350f187e7
 Author: Felix Fietkau <nbd@openwrt.org>
 Date:   Sat Feb 22 14:35:25 2014 +0100
@@ -1030,7 +1276,31 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
        else
                udelay(100);
  
-@@ -2051,9 +2051,8 @@ static bool ath9k_hw_set_power_awake(str
+@@ -1534,7 +1534,7 @@ EXPORT_SYMBOL(ath9k_hw_check_nav);
+ bool ath9k_hw_check_alive(struct ath_hw *ah)
+ {
+       int count = 50;
+-      u32 reg;
++      u32 reg, last_val;
+       if (AR_SREV_9300(ah))
+               return !ath9k_hw_detect_mac_hang(ah);
+@@ -1542,9 +1542,14 @@ bool ath9k_hw_check_alive(struct ath_hw 
+       if (AR_SREV_9285_12_OR_LATER(ah))
+               return true;
++      last_val = REG_READ(ah, AR_OBS_BUS_1);
+       do {
+               reg = REG_READ(ah, AR_OBS_BUS_1);
++              if (reg != last_val)
++                      return true;
++              udelay(1);
++              last_val = reg;
+               if ((reg & 0x7E7FFFEF) == 0x00702400)
+                       continue;
+@@ -2051,9 +2056,8 @@ static bool ath9k_hw_set_power_awake(str
  
        REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
                    AR_RTC_FORCE_WAKE_EN);
@@ -1043,6 +1313,33 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
  
 --- a/drivers/net/wireless/ath/ath9k/main.c
 +++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -451,7 +451,7 @@ void ath9k_tasklet(unsigned long data)
+                * interrupts are enabled in the reset routine.
+                */
+               atomic_inc(&ah->intr_ref_cnt);
+-              ath_dbg(common, ANY, "FATAL: Skipping interrupts\n");
++              ath_dbg(common, RESET, "FATAL: Skipping interrupts\n");
+               goto out;
+       }
+@@ -471,7 +471,7 @@ void ath9k_tasklet(unsigned long data)
+                        * interrupts are enabled in the reset routine.
+                        */
+                       atomic_inc(&ah->intr_ref_cnt);
+-                      ath_dbg(common, ANY,
++                      ath_dbg(common, RESET,
+                               "BB_WATCHDOG: Skipping interrupts\n");
+                       goto out;
+               }
+@@ -484,7 +484,7 @@ void ath9k_tasklet(unsigned long data)
+                       type = RESET_TYPE_TX_GTT;
+                       ath9k_queue_reset(sc, type);
+                       atomic_inc(&ah->intr_ref_cnt);
+-                      ath_dbg(common, ANY,
++                      ath_dbg(common, RESET,
+                               "GTT: Skipping interrupts\n");
+                       goto out;
+               }
 @@ -1866,7 +1866,7 @@ static void ath9k_set_coverage_class(str
  
  static bool ath9k_has_tx_pending(struct ath_softc *sc)
@@ -1333,7 +1630,17 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
  
        __sta_info_flush(sdata, true);
        ieee80211_free_keys(sdata, true);
-@@ -2638,6 +2643,24 @@ static int ieee80211_start_roc_work(stru
+@@ -1988,6 +1993,9 @@ static int ieee80211_change_bss(struct w
+       band = ieee80211_get_sdata_band(sdata);
++      if (WARN_ON(!wiphy->bands[band]))
++              return -EINVAL;
++
+       if (params->use_cts_prot >= 0) {
+               sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
+               changed |= BSS_CHANGED_ERP_CTS_PROT;
+@@ -2638,6 +2646,24 @@ static int ieee80211_start_roc_work(stru
        INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
        INIT_LIST_HEAD(&roc->dependents);
  
@@ -1358,7 +1665,7 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
        /* if there's one pending or we're scanning, queue this one */
        if (!list_empty(&local->roc_list) ||
            local->scanning || local->radar_detect_enabled)
-@@ -2772,24 +2795,6 @@ static int ieee80211_start_roc_work(stru
+@@ -2772,24 +2798,6 @@ static int ieee80211_start_roc_work(stru
        if (!queued)
                list_add_tail(&roc->list, &local->roc_list);
  
@@ -1383,7 +1690,7 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
        return 0;
  }
  
-@@ -3004,8 +3009,10 @@ void ieee80211_csa_finalize_work(struct 
+@@ -3004,8 +3012,10 @@ void ieee80211_csa_finalize_work(struct 
        if (!ieee80211_sdata_running(sdata))
                goto unlock;
  
@@ -1395,7 +1702,7 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
        err = ieee80211_vif_change_channel(sdata, &changed);
        mutex_unlock(&local->mtx);
        if (WARN_ON(err < 0))
-@@ -3022,13 +3029,13 @@ void ieee80211_csa_finalize_work(struct 
+@@ -3022,13 +3032,13 @@ void ieee80211_csa_finalize_work(struct 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
                err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
@@ -1412,7 +1719,7 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
                ieee80211_bss_info_change_notify(sdata, err);
                break;
        case NL80211_IFTYPE_ADHOC:
-@@ -3066,7 +3073,7 @@ int ieee80211_channel_switch(struct wiph
+@@ -3066,7 +3076,7 @@ int ieee80211_channel_switch(struct wiph
        struct ieee80211_if_mesh __maybe_unused *ifmsh;
        int err, num_chanctx;
  
@@ -1652,6 +1959,15 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
        return 0;
  }
  
+@@ -2900,7 +2912,7 @@ ieee80211_get_buffered_bc(struct ieee802
+                               cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+               }
+-              if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
++              if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
+               if (!ieee80211_tx_prepare(sdata, &tx, skb))
+                       break;
 --- a/net/mac80211/wpa.c
 +++ b/net/mac80211/wpa.c
 @@ -499,7 +499,7 @@ ieee80211_crypto_ccmp_decrypt(struct iee
@@ -1703,7 +2019,16 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
        /*
         * There are major locking problems in nl80211/mac80211 for CSA,
         * disable for all drivers until this has been reworked.
-@@ -875,8 +875,11 @@ static int cfg80211_netdev_notifier_call
+@@ -795,8 +795,6 @@ void cfg80211_leave(struct cfg80211_regi
+       default:
+               break;
+       }
+-
+-      wdev->beacon_interval = 0;
+ }
+ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
+@@ -875,8 +873,11 @@ static int cfg80211_netdev_notifier_call
                break;
        case NETDEV_DOWN:
                cfg80211_update_iface_num(rdev, wdev->iftype, -1);
@@ -3036,6 +3361,15 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
                buffered = ath_tid_has_buffered(tid);
  
                tid->sched = false;
+@@ -1696,7 +1698,7 @@ int ath_cabq_update(struct ath_softc *sc
+       ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
+-      qi.tqi_readyTime = (cur_conf->beacon_interval *
++      qi.tqi_readyTime = (TU_TO_USEC(cur_conf->beacon_interval) *
+                           ATH_CABQ_READY_TIME) / 100;
+       ath_txq_update(sc, qnum, &qi);
 @@ -2184,14 +2186,15 @@ int ath_tx_start(struct ieee80211_hw *hw
                txq->stopped = true;
        }
@@ -3301,7 +3635,79 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
                schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
 --- a/drivers/net/wireless/ath/ath9k/debug.c
 +++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -866,6 +866,12 @@ static ssize_t read_file_reset(struct fi
+@@ -138,43 +138,41 @@ static ssize_t read_file_ani(struct file
+       unsigned int len = 0, size = 1024;
+       ssize_t retval = 0;
+       char *buf;
++      int i;
++      struct {
++              const char *name;
++              unsigned int val;
++      } ani_info[] = {
++              { "ANI RESET", ah->stats.ast_ani_reset },
++              { "OFDM LEVEL", ah->ani.ofdmNoiseImmunityLevel },
++              { "CCK LEVEL", ah->ani.cckNoiseImmunityLevel },
++              { "SPUR UP", ah->stats.ast_ani_spurup },
++              { "SPUR DOWN", ah->stats.ast_ani_spurup },
++              { "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon },
++              { "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff },
++              { "MRC-CCK ON", ah->stats.ast_ani_ccklow },
++              { "MRC-CCK OFF", ah->stats.ast_ani_cckhigh },
++              { "FIR-STEP UP", ah->stats.ast_ani_stepup },
++              { "FIR-STEP DOWN", ah->stats.ast_ani_stepdown },
++              { "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero },
++              { "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs },
++              { "CCK ERRORS", ah->stats.ast_ani_cckerrs },
++      };
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+-      if (common->disable_ani) {
+-              len += scnprintf(buf + len, size - len, "%s: %s\n",
+-                               "ANI", "DISABLED");
++      len += scnprintf(buf + len, size - len, "%15s: %s\n", "ANI",
++                       common->disable_ani ? "DISABLED" : "ENABLED");
++
++      if (common->disable_ani)
+               goto exit;
+-      }
+-      len += scnprintf(buf + len, size - len, "%15s: %s\n",
+-                       "ANI", "ENABLED");
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "ANI RESET", ah->stats.ast_ani_reset);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "SPUR UP", ah->stats.ast_ani_spurup);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "SPUR DOWN", ah->stats.ast_ani_spurup);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "MRC-CCK ON", ah->stats.ast_ani_ccklow);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "MRC-CCK OFF", ah->stats.ast_ani_cckhigh);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "FIR-STEP UP", ah->stats.ast_ani_stepup);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "FIR-STEP DOWN", ah->stats.ast_ani_stepdown);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs);
+-      len += scnprintf(buf + len, size - len, "%15s: %u\n",
+-                       "CCK ERRORS", ah->stats.ast_ani_cckerrs);
++      for (i = 0; i < ARRAY_SIZE(ani_info); i++)
++              len += scnprintf(buf + len, size - len, "%15s: %u\n",
++                               ani_info[i].name, ani_info[i].val);
++
+ exit:
+       if (len > size)
+               len = size;
+@@ -866,6 +864,12 @@ static ssize_t read_file_reset(struct fi
                         "%17s: %2d\n", "PLL RX Hang",
                         sc->debug.stats.reset[RESET_TYPE_PLL_HANG]);
        len += scnprintf(buf + len, sizeof(buf) - len,
@@ -3327,3 +3733,380 @@ Date:   Thu Jan 23 20:06:34 2014 +0100
  
        if (rfMode & (AR_PHY_MODE_QUARTER | AR_PHY_MODE_HALF))
                REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL,
+--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
++++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+@@ -706,6 +706,7 @@ ath5k_get_survey(struct ieee80211_hw *hw
+       survey->channel = conf->chandef.chan;
+       survey->noise = ah->ah_noise_floor;
+       survey->filled = SURVEY_INFO_NOISE_DBM |
++                      SURVEY_INFO_IN_USE |
+                       SURVEY_INFO_CHANNEL_TIME |
+                       SURVEY_INFO_CHANNEL_TIME_BUSY |
+                       SURVEY_INFO_CHANNEL_TIME_RX |
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -732,11 +732,18 @@ static struct ath_rxbuf *ath_get_next_rx
+                       return NULL;
+               /*
+-               * mark descriptor as zero-length and set the 'more'
+-               * flag to ensure that both buffers get discarded
++               * Re-check previous descriptor, in case it has been filled
++               * in the mean time.
+                */
+-              rs->rs_datalen = 0;
+-              rs->rs_more = true;
++              ret = ath9k_hw_rxprocdesc(ah, ds, rs);
++              if (ret == -EINPROGRESS) {
++                      /*
++                       * mark descriptor as zero-length and set the 'more'
++                       * flag to ensure that both buffers get discarded
++                       */
++                      rs->rs_datalen = 0;
++                      rs->rs_more = true;
++              }
+       }
+       list_del(&bf->list);
+@@ -985,32 +992,32 @@ static int ath9k_rx_skb_preprocess(struc
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ieee80211_hdr *hdr;
+       bool discard_current = sc->rx.discard_next;
+-      int ret = 0;
+       /*
+        * Discard corrupt descriptors which are marked in
+        * ath_get_next_rx_buf().
+        */
+-      sc->rx.discard_next = rx_stats->rs_more;
+       if (discard_current)
+-              return -EINVAL;
++              goto corrupt;
++
++      sc->rx.discard_next = false;
+       /*
+        * Discard zero-length packets.
+        */
+       if (!rx_stats->rs_datalen) {
+               RX_STAT_INC(rx_len_err);
+-              return -EINVAL;
++              goto corrupt;
+       }
+-        /*
+-         * rs_status follows rs_datalen so if rs_datalen is too large
+-         * we can take a hint that hardware corrupted it, so ignore
+-         * those frames.
+-         */
++      /*
++       * rs_status follows rs_datalen so if rs_datalen is too large
++       * we can take a hint that hardware corrupted it, so ignore
++       * those frames.
++       */
+       if (rx_stats->rs_datalen > (common->rx_bufsize - ah->caps.rx_status_len)) {
+               RX_STAT_INC(rx_len_err);
+-              return -EINVAL;
++              goto corrupt;
+       }
+       /* Only use status info from the last fragment */
+@@ -1024,10 +1031,8 @@ static int ath9k_rx_skb_preprocess(struc
+        * This is different from the other corrupt descriptor
+        * condition handled above.
+        */
+-      if (rx_stats->rs_status & ATH9K_RXERR_CORRUPT_DESC) {
+-              ret = -EINVAL;
+-              goto exit;
+-      }
++      if (rx_stats->rs_status & ATH9K_RXERR_CORRUPT_DESC)
++              goto corrupt;
+       hdr = (struct ieee80211_hdr *) (skb->data + ah->caps.rx_status_len);
+@@ -1043,18 +1048,15 @@ static int ath9k_rx_skb_preprocess(struc
+               if (ath_process_fft(sc, hdr, rx_stats, rx_status->mactime))
+                       RX_STAT_INC(rx_spectral);
+-              ret = -EINVAL;
+-              goto exit;
++              return -EINVAL;
+       }
+       /*
+        * everything but the rate is checked here, the rate check is done
+        * separately to avoid doing two lookups for a rate for each frame.
+        */
+-      if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error)) {
+-              ret = -EINVAL;
+-              goto exit;
+-      }
++      if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error))
++              return -EINVAL;
+       if (ath_is_mybeacon(common, hdr)) {
+               RX_STAT_INC(rx_beacons);
+@@ -1064,15 +1066,11 @@ static int ath9k_rx_skb_preprocess(struc
+       /*
+        * This shouldn't happen, but have a safety check anyway.
+        */
+-      if (WARN_ON(!ah->curchan)) {
+-              ret = -EINVAL;
+-              goto exit;
+-      }
++      if (WARN_ON(!ah->curchan))
++              return -EINVAL;
+-      if (ath9k_process_rate(common, hw, rx_stats, rx_status)) {
+-              ret =-EINVAL;
+-              goto exit;
+-      }
++      if (ath9k_process_rate(common, hw, rx_stats, rx_status))
++              return -EINVAL;
+       ath9k_process_rssi(common, hw, rx_stats, rx_status);
+@@ -1087,9 +1085,11 @@ static int ath9k_rx_skb_preprocess(struc
+               sc->rx.num_pkts++;
+ #endif
+-exit:
+-      sc->rx.discard_next = false;
+-      return ret;
++      return 0;
++
++corrupt:
++      sc->rx.discard_next = rx_stats->rs_more;
++      return -EINVAL;
+ }
+ static void ath9k_rx_skb_postprocess(struct ath_common *common,
+--- a/drivers/net/wireless/ath/ath9k/ani.c
++++ b/drivers/net/wireless/ath/ath9k/ani.c
+@@ -176,16 +176,26 @@ static void ath9k_hw_set_ofdm_nil(struct
+       if (ah->opmode == NL80211_IFTYPE_STATION &&
+           BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH)
+               weak_sig = true;
+-
+       /*
+-       * OFDM Weak signal detection is always enabled for AP mode.
++       * Newer chipsets are better at dealing with high PHY error counts -
++       * keep weak signal detection enabled when no RSSI threshold is
++       * available to determine if it is needed (mode != STA)
+        */
+-      if (ah->opmode != NL80211_IFTYPE_AP &&
+-          aniState->ofdmWeakSigDetect != weak_sig) {
+-              ath9k_hw_ani_control(ah,
+-                                   ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+-                                   entry_ofdm->ofdm_weak_signal_on);
+-      }
++      else if (AR_SREV_9300_20_OR_LATER(ah) &&
++               ah->opmode != NL80211_IFTYPE_STATION)
++              weak_sig = true;
++
++      /* Older chipsets are more sensitive to high PHY error counts */
++      else if (!AR_SREV_9300_20_OR_LATER(ah) &&
++               aniState->ofdmNoiseImmunityLevel >= 8)
++              weak_sig = false;
++
++      if (aniState->ofdmWeakSigDetect != weak_sig)
++              ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
++                                   weak_sig);
++
++      if (!AR_SREV_9300_20_OR_LATER(ah))
++              return;
+       if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) {
+               ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
+@@ -308,17 +318,6 @@ void ath9k_ani_reset(struct ath_hw *ah, 
+       BUG_ON(aniState == NULL);
+       ah->stats.ast_ani_reset++;
+-      /* only allow a subset of functions in AP mode */
+-      if (ah->opmode == NL80211_IFTYPE_AP) {
+-              if (IS_CHAN_2GHZ(chan)) {
+-                      ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL |
+-                                          ATH9K_ANI_FIRSTEP_LEVEL);
+-                      if (AR_SREV_9300_20_OR_LATER(ah))
+-                              ah->ani_function |= ATH9K_ANI_MRC_CCK;
+-              } else
+-                      ah->ani_function = 0;
+-      }
+-
+       ofdm_nil = max_t(int, ATH9K_ANI_OFDM_DEF_LEVEL,
+                        aniState->ofdmNoiseImmunityLevel);
+       cck_nil = max_t(int, ATH9K_ANI_CCK_DEF_LEVEL,
+@@ -483,10 +482,17 @@ void ath9k_hw_ani_init(struct ath_hw *ah
+       ath_dbg(common, ANI, "Initialize ANI\n");
+-      ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
+-      ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;
+-      ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;
+-      ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;
++      if (AR_SREV_9300_20_OR_LATER(ah)) {
++              ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
++              ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;
++              ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;
++              ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;
++      } else {
++              ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_OLD;
++              ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_OLD;
++              ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_OLD;
++              ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_OLD;
++      }
+       ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
+       ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
+--- a/drivers/net/wireless/ath/ath9k/ani.h
++++ b/drivers/net/wireless/ath/ath9k/ani.h
+@@ -22,12 +22,16 @@
+ /* units are errors per second */
+ #define ATH9K_ANI_OFDM_TRIG_HIGH           3500
+ #define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000
++#define ATH9K_ANI_OFDM_TRIG_HIGH_OLD       500
+ #define ATH9K_ANI_OFDM_TRIG_LOW           400
+ #define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900
++#define ATH9K_ANI_OFDM_TRIG_LOW_OLD       200
+ #define ATH9K_ANI_CCK_TRIG_HIGH           600
++#define ATH9K_ANI_CCK_TRIG_HIGH_OLD       200
+ #define ATH9K_ANI_CCK_TRIG_LOW            300
++#define ATH9K_ANI_CCK_TRIG_LOW_OLD        100
+ #define ATH9K_ANI_SPUR_IMMUNE_LVL         3
+ #define ATH9K_ANI_FIRSTEP_LVL             2
+--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+@@ -26,10 +26,6 @@ static const int firstep_table[] =
+ /* level:  0   1   2   3   4   5   6   7   8  */
+       { -4, -2,  0,  2,  4,  6,  8, 10, 12 }; /* lvl 0-8, default 2 */
+-static const int cycpwrThr1_table[] =
+-/* level:  0   1   2   3   4   5   6   7   8  */
+-      { -6, -4, -2,  0,  2,  4,  6,  8 };     /* lvl 0-7, default 3 */
+-
+ /*
+  * register values to turn OFDM weak signal detection OFF
+  */
+@@ -921,7 +917,7 @@ static bool ar5008_hw_ani_control_new(st
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ath9k_channel *chan = ah->curchan;
+       struct ar5416AniState *aniState = &ah->ani;
+-      s32 value, value2;
++      s32 value;
+       switch (cmd & ah->ani_function) {
+       case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{
+@@ -1008,42 +1004,11 @@ static bool ar5008_hw_ani_control_new(st
+       case ATH9K_ANI_FIRSTEP_LEVEL:{
+               u32 level = param;
+-              if (level >= ARRAY_SIZE(firstep_table)) {
+-                      ath_dbg(common, ANI,
+-                              "ATH9K_ANI_FIRSTEP_LEVEL: level out of range (%u > %zu)\n",
+-                              level, ARRAY_SIZE(firstep_table));
+-                      return false;
+-              }
+-
+-              /*
+-               * make register setting relative to default
+-               * from INI file & cap value
+-               */
+-              value = firstep_table[level] -
+-                      firstep_table[ATH9K_ANI_FIRSTEP_LVL] +
+-                      aniState->iniDef.firstep;
+-              if (value < ATH9K_SIG_FIRSTEP_SETTING_MIN)
+-                      value = ATH9K_SIG_FIRSTEP_SETTING_MIN;
+-              if (value > ATH9K_SIG_FIRSTEP_SETTING_MAX)
+-                      value = ATH9K_SIG_FIRSTEP_SETTING_MAX;
++              value = level * 2;
+               REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
+-                            AR_PHY_FIND_SIG_FIRSTEP,
+-                            value);
+-              /*
+-               * we need to set first step low register too
+-               * make register setting relative to default
+-               * from INI file & cap value
+-               */
+-              value2 = firstep_table[level] -
+-                       firstep_table[ATH9K_ANI_FIRSTEP_LVL] +
+-                       aniState->iniDef.firstepLow;
+-              if (value2 < ATH9K_SIG_FIRSTEP_SETTING_MIN)
+-                      value2 = ATH9K_SIG_FIRSTEP_SETTING_MIN;
+-              if (value2 > ATH9K_SIG_FIRSTEP_SETTING_MAX)
+-                      value2 = ATH9K_SIG_FIRSTEP_SETTING_MAX;
+-
++                            AR_PHY_FIND_SIG_FIRSTEP, value);
+               REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW,
+-                            AR_PHY_FIND_SIG_FIRSTEP_LOW, value2);
++                            AR_PHY_FIND_SIG_FIRSTEP_LOW, value);
+               if (level != aniState->firstepLevel) {
+                       ath_dbg(common, ANI,
+@@ -1060,7 +1025,7 @@ static bool ar5008_hw_ani_control_new(st
+                               aniState->firstepLevel,
+                               level,
+                               ATH9K_ANI_FIRSTEP_LVL,
+-                              value2,
++                              value,
+                               aniState->iniDef.firstepLow);
+                       if (level > aniState->firstepLevel)
+                               ah->stats.ast_ani_stepup++;
+@@ -1073,41 +1038,13 @@ static bool ar5008_hw_ani_control_new(st
+       case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{
+               u32 level = param;
+-              if (level >= ARRAY_SIZE(cycpwrThr1_table)) {
+-                      ath_dbg(common, ANI,
+-                              "ATH9K_ANI_SPUR_IMMUNITY_LEVEL: level out of range (%u > %zu)\n",
+-                              level, ARRAY_SIZE(cycpwrThr1_table));
+-                      return false;
+-              }
+-              /*
+-               * make register setting relative to default
+-               * from INI file & cap value
+-               */
+-              value = cycpwrThr1_table[level] -
+-                      cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] +
+-                      aniState->iniDef.cycpwrThr1;
+-              if (value < ATH9K_SIG_SPUR_IMM_SETTING_MIN)
+-                      value = ATH9K_SIG_SPUR_IMM_SETTING_MIN;
+-              if (value > ATH9K_SIG_SPUR_IMM_SETTING_MAX)
+-                      value = ATH9K_SIG_SPUR_IMM_SETTING_MAX;
++              value = (level + 1) * 2;
+               REG_RMW_FIELD(ah, AR_PHY_TIMING5,
+-                            AR_PHY_TIMING5_CYCPWR_THR1,
+-                            value);
++                            AR_PHY_TIMING5_CYCPWR_THR1, value);
+-              /*
+-               * set AR_PHY_EXT_CCA for extension channel
+-               * make register setting relative to default
+-               * from INI file & cap value
+-               */
+-              value2 = cycpwrThr1_table[level] -
+-                       cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] +
+-                       aniState->iniDef.cycpwrThr1Ext;
+-              if (value2 < ATH9K_SIG_SPUR_IMM_SETTING_MIN)
+-                      value2 = ATH9K_SIG_SPUR_IMM_SETTING_MIN;
+-              if (value2 > ATH9K_SIG_SPUR_IMM_SETTING_MAX)
+-                      value2 = ATH9K_SIG_SPUR_IMM_SETTING_MAX;
+-              REG_RMW_FIELD(ah, AR_PHY_EXT_CCA,
+-                            AR_PHY_EXT_TIMING5_CYCPWR_THR1, value2);
++              if (IS_CHAN_HT40(ah->curchan))
++                      REG_RMW_FIELD(ah, AR_PHY_EXT_CCA,
++                                    AR_PHY_EXT_TIMING5_CYCPWR_THR1, value);
+               if (level != aniState->spurImmunityLevel) {
+                       ath_dbg(common, ANI,
+@@ -1124,7 +1061,7 @@ static bool ar5008_hw_ani_control_new(st
+                               aniState->spurImmunityLevel,
+                               level,
+                               ATH9K_ANI_SPUR_IMMUNE_LVL,
+-                              value2,
++                              value,
+                               aniState->iniDef.cycpwrThr1Ext);
+                       if (level > aniState->spurImmunityLevel)
+                               ah->stats.ast_ani_spurup++;