+@@ -2633,6 +2639,16 @@ enum ieee80211_roc_type {
+ * @ipv6_addr_change: IPv6 address assignment on the given interface changed.
+ * Currently, this is only called for managed or P2P client interfaces.
+ * This callback is optional; it must not sleep.
++ *
++ * @channel_switch_beacon: Starts a channel switch to a new channel.
++ * Beacons are modified to include CSA or ECSA IEs before calling this
++ * function. The corresponding count fields in these IEs must be
++ * decremented, and when they reach zero the driver must call
++ * ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
++ * get the csa counter decremented by mac80211, but must check if it is
++ * zero using ieee80211_csa_is_complete() after the beacon has been
++ * transmitted and then call ieee80211_csa_finish().
++ *
+ */
+ struct ieee80211_ops {
+ void (*tx)(struct ieee80211_hw *hw,
+@@ -2830,6 +2846,9 @@ struct ieee80211_ops {
+ struct ieee80211_vif *vif,
+ struct inet6_dev *idev);
+ #endif
++ void (*channel_switch_beacon)(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ struct cfg80211_chan_def *chandef);
+ };
+
+ /**
+@@ -3325,6 +3344,25 @@ static inline struct sk_buff *ieee80211_
+ }
+
+ /**
++ * ieee80211_csa_finish - notify mac80211 about channel switch
++ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
++ *
++ * After a channel switch announcement was scheduled and the counter in this
++ * announcement hit zero, this function must be called by the driver to
++ * notify mac80211 that the channel can be changed.
++ */
++void ieee80211_csa_finish(struct ieee80211_vif *vif);
++
++/**
++ * ieee80211_csa_is_complete - find out if counters reached zero
++ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
++ *
++ * This function returns whether the channel switch counters reached zero.
++ */
++bool ieee80211_csa_is_complete(struct ieee80211_vif *vif);
++
++
++/**
+ * ieee80211_proberesp_get - retrieve a Probe Response template
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -854,8 +854,8 @@ static int ieee80211_set_probe_resp(stru
+ return 0;
+ }
+
+-static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+- struct cfg80211_beacon_data *params)
++int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
++ struct cfg80211_beacon_data *params)
+ {
+ struct beacon_data *new, *old;
+ int new_head_len, new_tail_len;
+@@ -1018,6 +1018,12 @@ static int ieee80211_change_beacon(struc
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
++ /* don't allow changing the beacon while CSA is in place - offset
++ * of channel switch counter may change
++ */
++ if (sdata->vif.csa_active)
++ return -EBUSY;
++
+ old = rtnl_dereference(sdata->u.ap.beacon);
+ if (!old)
+ return -ENOENT;
+@@ -1042,6 +1048,10 @@ static int ieee80211_stop_ap(struct wiph
+ return -ENOENT;
+ old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
+
++ /* abort any running channel switch */
++ sdata->vif.csa_active = false;
++ cancel_work_sync(&sdata->csa_finalize_work);
++
+ /* turn off carrier for this interface and dependent VLANs */
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ netif_carrier_off(vlan->dev);
+@@ -2784,6 +2794,178 @@ static int ieee80211_start_radar_detecti
+ return 0;
+ }
+
++static struct cfg80211_beacon_data *
++cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
++{
++ struct cfg80211_beacon_data *new_beacon;
++ u8 *pos;
++ int len;
++
++ len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
++ beacon->proberesp_ies_len + beacon->assocresp_ies_len +
++ beacon->probe_resp_len;
++
++ new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
++ if (!new_beacon)
++ return NULL;
++
++ pos = (u8 *)(new_beacon + 1);
++ if (beacon->head_len) {
++ new_beacon->head_len = beacon->head_len;
++ new_beacon->head = pos;
++ memcpy(pos, beacon->head, beacon->head_len);
++ pos += beacon->head_len;
++ }
++ if (beacon->tail_len) {
++ new_beacon->tail_len = beacon->tail_len;
++ new_beacon->tail = pos;
++ memcpy(pos, beacon->tail, beacon->tail_len);
++ pos += beacon->tail_len;
++ }
++ if (beacon->beacon_ies_len) {
++ new_beacon->beacon_ies_len = beacon->beacon_ies_len;
++ new_beacon->beacon_ies = pos;
++ memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
++ pos += beacon->beacon_ies_len;
++ }
++ if (beacon->proberesp_ies_len) {
++ new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
++ new_beacon->proberesp_ies = pos;
++ memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
++ pos += beacon->proberesp_ies_len;
++ }
++ if (beacon->assocresp_ies_len) {
++ new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
++ new_beacon->assocresp_ies = pos;
++ memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
++ pos += beacon->assocresp_ies_len;
++ }
++ if (beacon->probe_resp_len) {
++ new_beacon->probe_resp_len = beacon->probe_resp_len;
++ beacon->probe_resp = pos;
++ memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
++ pos += beacon->probe_resp_len;
++ }
++
++ return new_beacon;
++}
++
++void ieee80211_csa_finalize_work(struct work_struct *work)
++{
++ struct ieee80211_sub_if_data *sdata =
++ container_of(work, struct ieee80211_sub_if_data,
++ csa_finalize_work);
++ struct ieee80211_local *local = sdata->local;
++ int err, changed;
++
++ if (!ieee80211_sdata_running(sdata))
++ return;
++
++ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
++ return;
++
++ sdata->radar_required = sdata->csa_radar_required;
++ err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
++ &changed);
++ if (WARN_ON(err < 0))
++ return;
++
++ err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
++ if (err < 0)
++ return;
++
++ changed |= err;
++ kfree(sdata->u.ap.next_beacon);
++ sdata->u.ap.next_beacon = NULL;
++ sdata->vif.csa_active = false;
++
++ ieee80211_wake_queues_by_reason(&sdata->local->hw,
++ IEEE80211_MAX_QUEUE_MAP,
++ IEEE80211_QUEUE_STOP_REASON_CSA);
++
++ ieee80211_bss_info_change_notify(sdata, changed);
++
++ cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
++}
++
++static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
++ struct cfg80211_csa_settings *params)
++{
++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_chanctx_conf *chanctx_conf;
++ struct ieee80211_chanctx *chanctx;
++ int err, num_chanctx;
++
++ if (!list_empty(&local->roc_list) || local->scanning)
++ return -EBUSY;
++
++ if (sdata->wdev.cac_started)
++ return -EBUSY;
++
++ if (cfg80211_chandef_identical(¶ms->chandef,
++ &sdata->vif.bss_conf.chandef))
++ return -EINVAL;
++
++ rcu_read_lock();
++ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
++ if (!chanctx_conf) {
++ rcu_read_unlock();
++ return -EBUSY;
++ }
++
++ /* don't handle for multi-VIF cases */
++ chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
++ if (chanctx->refcount > 1) {
++ rcu_read_unlock();
++ return -EBUSY;
++ }
++ num_chanctx = 0;
++ list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
++ num_chanctx++;
++ rcu_read_unlock();
++
++ if (num_chanctx > 1)
++ return -EBUSY;
++
++ /* don't allow another channel switch if one is already active. */
++ if (sdata->vif.csa_active)
++ return -EBUSY;
++
++ /* only handle AP for now. */
++ switch (sdata->vif.type) {
++ case NL80211_IFTYPE_AP:
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after);
++ if (!sdata->u.ap.next_beacon)
++ return -ENOMEM;
++
++ sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
++ sdata->csa_counter_offset_presp = params->counter_offset_presp;
++ sdata->csa_radar_required = params->radar_required;
++
++ if (params->block_tx)
++ ieee80211_stop_queues_by_reason(&local->hw,
++ IEEE80211_MAX_QUEUE_MAP,
++ IEEE80211_QUEUE_STOP_REASON_CSA);
++
++ err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa);
++ if (err < 0)
++ return err;
++
++ local->csa_chandef = params->chandef;
++ sdata->vif.csa_active = true;
++
++ ieee80211_bss_info_change_notify(sdata, err);
++ drv_channel_switch_beacon(sdata, ¶ms->chandef);
++
++ return 0;
++}
++
+ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *chan, bool offchan,
+ unsigned int wait, const u8 *buf, size_t len,
+@@ -3501,4 +3683,5 @@ struct cfg80211_ops mac80211_config_ops
+ .get_et_strings = ieee80211_get_et_strings,
+ .get_channel = ieee80211_cfg_get_channel,
+ .start_radar_detection = ieee80211_start_radar_detection,
++ .channel_switch = ieee80211_channel_switch,
+ };
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -410,6 +410,64 @@ int ieee80211_vif_use_channel(struct iee
+ return ret;
+ }
+
++int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
++ const struct cfg80211_chan_def *chandef,
++ u32 *changed)
++{
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_chanctx_conf *conf;
++ struct ieee80211_chanctx *ctx;
++ int ret;
++ u32 chanctx_changed = 0;
++
++ /* should never be called if not performing a channel switch. */
++ if (WARN_ON(!sdata->vif.csa_active))
++ return -EINVAL;
++
++ if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
++ IEEE80211_CHAN_DISABLED))
++ return -EINVAL;
++
++ mutex_lock(&local->chanctx_mtx);
++ conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
++ lockdep_is_held(&local->chanctx_mtx));
++ if (!conf) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ ctx = container_of(conf, struct ieee80211_chanctx, conf);
++ if (ctx->refcount != 1) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (sdata->vif.bss_conf.chandef.width != chandef->width) {
++ chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
++ *changed |= BSS_CHANGED_BANDWIDTH;
++ }
++
++ sdata->vif.bss_conf.chandef = *chandef;
++ ctx->conf.def = *chandef;
++
++ chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
++ drv_change_chanctx(local, ctx, chanctx_changed);
++
++ if (!local->use_chanctx) {
++ local->_oper_chandef = *chandef;
++ ieee80211_hw_config(local, 0);
++ }
++
++ ieee80211_recalc_chanctx_chantype(local, ctx);
++ ieee80211_recalc_smps_chanctx(local, ctx);
++ ieee80211_recalc_radar_chanctx(local, ctx);
++
++ ret = 0;
++ out:
++ mutex_unlock(&local->chanctx_mtx);
++ return ret;
++}
++
+ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ u32 *changed)
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -1104,4 +1104,17 @@ static inline void drv_ipv6_addr_change(
+ }
+ #endif
+
++static inline void
++drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
++ struct cfg80211_chan_def *chandef)
++{
++ struct ieee80211_local *local = sdata->local;
++
++ if (local->ops->channel_switch_beacon) {
++ trace_drv_channel_switch_beacon(local, sdata, chandef);
++ local->ops->channel_switch_beacon(&local->hw, &sdata->vif,
++ chandef);
++ }
++}
++
+ #endif /* __MAC80211_DRIVER_OPS */
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -53,9 +53,6 @@ struct ieee80211_local;
+ * increased memory use (about 2 kB of RAM per entry). */
+ #define IEEE80211_FRAGMENT_MAX 4
+
+-#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
+-#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
+-
+ /* power level hasn't been configured (or set to automatic) */
+ #define IEEE80211_UNSET_POWER_LEVEL INT_MIN
+
+@@ -259,6 +256,8 @@ struct ieee80211_if_ap {
+ struct beacon_data __rcu *beacon;
+ struct probe_resp __rcu *probe_resp;
+
++ /* to be used after channel switch. */
++ struct cfg80211_beacon_data *next_beacon;
+ struct list_head vlans;
+
+ struct ps_data ps;
+@@ -713,6 +712,11 @@ struct ieee80211_sub_if_data {
+
+ struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
+
++ struct work_struct csa_finalize_work;
++ int csa_counter_offset_beacon;
++ int csa_counter_offset_presp;
++ bool csa_radar_required;
++
+ /* used to reconfigure hardware SM PS */
+ struct work_struct recalc_smps;
+
+@@ -1346,6 +1350,9 @@ void ieee80211_roc_notify_destroy(struct
+ void ieee80211_sw_roc_work(struct work_struct *work);
+ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
+
++/* channel switch handling */
++void ieee80211_csa_finalize_work(struct work_struct *work);
++
+ /* interface handling */
+ int ieee80211_iface_init(void);
+ void ieee80211_iface_exit(void);
+@@ -1367,6 +1374,8 @@ void ieee80211_del_virtual_monitor(struc
+
+ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
++int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
++ struct cfg80211_beacon_data *params);
+
+ static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
+ {
+@@ -1627,6 +1636,11 @@ int __must_check
+ ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ u32 *changed);
++/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
++int __must_check
++ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
++ const struct cfg80211_chan_def *chandef,
++ u32 *changed);
+ void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
+ void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
+ void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -1906,6 +1906,32 @@ TRACE_EVENT(api_radar_detected,
+ )
+ );
+
++TRACE_EVENT(drv_channel_switch_beacon,
++ TP_PROTO(struct ieee80211_local *local,
++ struct ieee80211_sub_if_data *sdata,
++ struct cfg80211_chan_def *chandef),
++
++ TP_ARGS(local, sdata, chandef),
++
++ TP_STRUCT__entry(
++ LOCAL_ENTRY
++ VIF_ENTRY
++ CHANDEF_ENTRY
++ ),
++
++ TP_fast_assign(
++ LOCAL_ASSIGN;
++ VIF_ASSIGN;
++ CHANDEF_ASSIGN(chandef);
++ ),
++
++ TP_printk(
++ LOCAL_PR_FMT VIF_PR_FMT " channel switch to " CHANDEF_PR_FMT,
++ LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG
++ )
++);
++
++
+ #ifdef CPTCFG_MAC80211_MESSAGE_TRACING
+ #undef TRACE_SYSTEM
+ #define TRACE_SYSTEM mac80211_msg
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -2326,6 +2326,81 @@ static int ieee80211_beacon_add_tim(stru
+ return 0;
+ }
+
++void ieee80211_csa_finish(struct ieee80211_vif *vif)
++{
++ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
++
++ ieee80211_queue_work(&sdata->local->hw,
++ &sdata->csa_finalize_work);
++}
++EXPORT_SYMBOL(ieee80211_csa_finish);
++
++static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
++ struct beacon_data *beacon)
++{
++ struct probe_resp *resp;
++ int counter_offset_beacon = sdata->csa_counter_offset_beacon;
++ int counter_offset_presp = sdata->csa_counter_offset_presp;
++
++ /* warn if the driver did not check for/react to csa completeness */
++ if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
++ return;
++
++ ((u8 *)beacon->tail)[counter_offset_beacon]--;
++
++ if (sdata->vif.type == NL80211_IFTYPE_AP &&
++ counter_offset_presp) {
++ rcu_read_lock();
++ resp = rcu_dereference(sdata->u.ap.probe_resp);
++
++ /* if nl80211 accepted the offset, this should not happen. */
++ if (WARN_ON(!resp)) {
++ rcu_read_unlock();
++ return;
++ }
++ resp->data[counter_offset_presp]--;
++ rcu_read_unlock();
++ }
++}
++
++bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
++{
++ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
++ struct beacon_data *beacon = NULL;
++ u8 *beacon_data;
++ size_t beacon_data_len;
++ int counter_beacon = sdata->csa_counter_offset_beacon;
++ int ret = false;
++
++ if (!ieee80211_sdata_running(sdata))
++ return false;
++
++ rcu_read_lock();
++ if (vif->type == NL80211_IFTYPE_AP) {
++ struct ieee80211_if_ap *ap = &sdata->u.ap;
++
++ beacon = rcu_dereference(ap->beacon);
++ if (WARN_ON(!beacon || !beacon->tail))
++ goto out;
++ beacon_data = beacon->tail;
++ beacon_data_len = beacon->tail_len;
++ } else {
++ WARN_ON(1);
++ goto out;
++ }
++
++ if (WARN_ON(counter_beacon > beacon_data_len))
++ goto out;
++
++ if (beacon_data[counter_beacon] == 0)
++ ret = true;
++ out:
++ rcu_read_unlock();
++
++ return ret;
++}
++EXPORT_SYMBOL(ieee80211_csa_is_complete);
++
+ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u16 *tim_offset, u16 *tim_length)
+@@ -2356,6 +2431,9 @@ struct sk_buff *ieee80211_beacon_get_tim
+ struct beacon_data *beacon = rcu_dereference(ap->beacon);
+
+ if (beacon) {
++ if (sdata->vif.csa_active)
++ ieee80211_update_csa(sdata, beacon);
++
+ /*
+ * headroom, head length,
+ * tail length and maximum TIM length
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -923,4 +923,16 @@ static inline void rdev_crit_proto_stop(
+ trace_rdev_return_void(&rdev->wiphy);
+ }
+
++static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,
++ struct net_device *dev,
++ struct cfg80211_csa_settings *params)
++{
++ int ret;
++
++ trace_rdev_channel_switch(&rdev->wiphy, dev, params);
++ ret = rdev->ops->channel_switch(&rdev->wiphy, dev, params);
++ trace_rdev_return_int(&rdev->wiphy, ret);
++ return ret;
++}
++
+ #endif /* __CFG80211_RDEV_OPS */
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -1841,6 +1841,39 @@ TRACE_EVENT(rdev_crit_proto_stop,
+ WIPHY_PR_ARG, WDEV_PR_ARG)
+ );
+
++TRACE_EVENT(rdev_channel_switch,
++ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
++ struct cfg80211_csa_settings *params),
++ TP_ARGS(wiphy, netdev, params),
++ TP_STRUCT__entry(
++ WIPHY_ENTRY
++ NETDEV_ENTRY
++ CHAN_DEF_ENTRY
++ __field(u16, counter_offset_beacon)
++ __field(u16, counter_offset_presp)
++ __field(bool, radar_required)
++ __field(bool, block_tx)
++ __field(u8, count)
++ ),
++ TP_fast_assign(
++ WIPHY_ASSIGN;
++ NETDEV_ASSIGN;
++ CHAN_DEF_ASSIGN(¶ms->chandef);
++ __entry->counter_offset_beacon = params->counter_offset_beacon;
++ __entry->counter_offset_presp = params->counter_offset_presp;
++ __entry->radar_required = params->radar_required;
++ __entry->block_tx = params->block_tx;
++ __entry->count = params->count;
++ ),
++ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
++ ", block_tx: %d, count: %u, radar_required: %d"
++ ", counter offsets (beacon/presp): %u/%u",
++ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
++ __entry->block_tx, __entry->count, __entry->radar_required,
++ __entry->counter_offset_beacon,
++ __entry->counter_offset_presp)
++);
++
+ /*************************************************************
+ * cfg80211 exported functions traces *
+ *************************************************************/
+--- a/drivers/net/wireless/ath/ath.h
++++ b/drivers/net/wireless/ath/ath.h
+@@ -159,7 +159,7 @@ struct ath_common {
+
+ bool btcoex_enabled;
+ bool disable_ani;
+- bool antenna_diversity;
++ bool bt_ant_diversity;
+ };
+
+ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
+--- a/drivers/net/wireless/ath/ath9k/antenna.c
++++ b/drivers/net/wireless/ath/ath9k/antenna.c
+@@ -16,37 +16,119 @@
+
+ #include "ath9k.h"
+
+-static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta,
++/*
++ * AR9285
++ * ======
++ *
++ * EEPROM has 2 4-bit fields containing the card configuration.
++ *
++ * antdiv_ctl1:
++ * ------------
++ * bb_enable_ant_div_lnadiv : 1
++ * bb_ant_div_alt_gaintb : 1
++ * bb_ant_div_main_gaintb : 1
++ * bb_enable_ant_fast_div : 1
++ *
++ * antdiv_ctl2:
++ * -----------
++ * bb_ant_div_alt_lnaconf : 2
++ * bb_ant_div_main_lnaconf : 2
++ *
++ * The EEPROM bits are used as follows:
++ * ------------------------------------
++ *
++ * bb_enable_ant_div_lnadiv - Enable LNA path rx antenna diversity/combining.
++ * Set in AR_PHY_MULTICHAIN_GAIN_CTL.
++ *
++ * bb_ant_div_[alt/main]_gaintb - 0 -> Antenna config Alt/Main uses gaintable 0
++ * 1 -> Antenna config Alt/Main uses gaintable 1
++ * Set in AR_PHY_MULTICHAIN_GAIN_CTL.
++ *
++ * bb_enable_ant_fast_div - Enable fast antenna diversity.
++ * Set in AR_PHY_CCK_DETECT.
++ *
++ * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config.
++ * Set in AR_PHY_MULTICHAIN_GAIN_CTL.
++ * 10=LNA1
++ * 01=LNA2
++ * 11=LNA1+LNA2
++ * 00=LNA1-LNA2
++ *
++ * AR9485 / AR9565 / AR9331
++ * ========================
++ *
++ * The same bits are present in the EEPROM, but the location in the
++ * EEPROM is different (ant_div_control in ar9300_BaseExtension_1).
++ *
++ * ant_div_alt_lnaconf ==> bit 0~1
++ * ant_div_main_lnaconf ==> bit 2~3
++ * ant_div_alt_gaintb ==> bit 4
++ * ant_div_main_gaintb ==> bit 5
++ * enable_ant_div_lnadiv ==> bit 6
++ * enable_ant_fast_div ==> bit 7
++ */
++
++static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb,
++ int alt_ratio, int maxdelta,
+ int mindelta, int main_rssi_avg,
+ int alt_rssi_avg, int pkt_count)
+ {
+- return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+- (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
+- (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
++ if (pkt_count <= 50)
++ return false;
++
++ if (alt_rssi_avg > main_rssi_avg + mindelta)
++ return true;
++
++ if (alt_ratio >= antcomb->ant_ratio2 &&
++ alt_rssi_avg >= antcomb->low_rssi_thresh &&
++ (alt_rssi_avg > main_rssi_avg + maxdelta))
++ return true;
++
++ return false;
+ }
+
+-static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
+- int curr_main_set, int curr_alt_set,
+- int alt_rssi_avg, int main_rssi_avg)
++static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf,
++ struct ath_ant_comb *antcomb,
++ int alt_ratio, int alt_rssi_avg,
++ int main_rssi_avg)
+ {
+- bool result = false;
+- switch (div_group) {
++ bool result, set1, set2;
++
++ result = set1 = set2 = false;
++
++ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 &&
++ conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1)
++ set1 = true;
++
++ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 &&
++ conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2)
++ set2 = true;
++
++ switch (conf->div_group) {
+ case 0:
+ if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
+ result = true;
+ break;
+ case 1:
+ case 2:
+- if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
+- (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
+- (alt_rssi_avg >= (main_rssi_avg - 5))) ||
+- ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) &&
+- (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) &&
+- (alt_rssi_avg >= (main_rssi_avg - 2)))) &&
+- (alt_rssi_avg >= 4))
++ if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
++ break;
++
++ if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) ||
++ (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) ||
++ (alt_ratio > antcomb->ant_ratio))
+ result = true;
+- else
+- result = false;
++
++ break;
++ case 3:
++ if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
++ break;
++
++ if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) ||
++ (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) ||
++ (alt_ratio > antcomb->ant_ratio))
++ result = true;
++
+ break;
+ }
+
+@@ -108,6 +190,74 @@ static void ath_lnaconf_alt_good_scan(st
+ }
+ }
+
++static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb,
++ struct ath_hw_antcomb_conf *conf)
++{
++ /* set alt to the conf with maximun ratio */
++ if (antcomb->first_ratio && antcomb->second_ratio) {
++ if (antcomb->rssi_second > antcomb->rssi_third) {
++ /* first alt*/
++ if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
++ (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
++ /* Set alt LNA1 or LNA2*/
++ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ else
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ else
++ /* Set alt to A+B or A-B */
++ conf->alt_lna_conf =
++ antcomb->first_quick_scan_conf;
++ } else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
++ (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) {
++ /* Set alt LNA1 or LNA2 */
++ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ else
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ } else {
++ /* Set alt to A+B or A-B */
++ conf->alt_lna_conf = antcomb->second_quick_scan_conf;
++ }
++ } else if (antcomb->first_ratio) {
++ /* first alt */
++ if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
++ (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
++ /* Set alt LNA1 or LNA2 */
++ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ else
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ else
++ /* Set alt to A+B or A-B */
++ conf->alt_lna_conf = antcomb->first_quick_scan_conf;
++ } else if (antcomb->second_ratio) {
++ /* second alt */
++ if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
++ (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
++ /* Set alt LNA1 or LNA2 */
++ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ else
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ else
++ /* Set alt to A+B or A-B */
++ conf->alt_lna_conf = antcomb->second_quick_scan_conf;
++ } else {
++ /* main is largest */
++ if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
++ (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
++ /* Set alt LNA1 or LNA2 */
++ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ else
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ else
++ /* Set alt to A+B or A-B */
++ conf->alt_lna_conf = antcomb->main_conf;
++ }
++}
++
+ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
+ struct ath_hw_antcomb_conf *div_ant_conf,
+ int main_rssi_avg, int alt_rssi_avg,
+@@ -129,7 +279,7 @@ static void ath_select_ant_div_from_quic
+
+ if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
+ /* main is LNA1 */
+- if (ath_is_alt_ant_ratio_better(alt_ratio,
++ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
+ ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+ ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+ main_rssi_avg, alt_rssi_avg,
+@@ -138,7 +288,7 @@ static void ath_select_ant_div_from_quic
+ else
+ antcomb->first_ratio = false;
+ } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
+- if (ath_is_alt_ant_ratio_better(alt_ratio,
++ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
+ ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
+ ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+ main_rssi_avg, alt_rssi_avg,
+@@ -147,11 +297,11 @@ static void ath_select_ant_div_from_quic
+ else
+ antcomb->first_ratio = false;
+ } else {
+- if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+- (alt_rssi_avg > main_rssi_avg +
+- ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
+- (alt_rssi_avg > main_rssi_avg)) &&
+- (antcomb->total_pkt_count > 50))
++ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
++ ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
++ 0,
++ main_rssi_avg, alt_rssi_avg,
++ antcomb->total_pkt_count))
+ antcomb->first_ratio = true;
+ else
+ antcomb->first_ratio = false;
+@@ -164,17 +314,21 @@ static void ath_select_ant_div_from_quic
+ antcomb->rssi_first = main_rssi_avg;
+ antcomb->rssi_third = alt_rssi_avg;
+
+- if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
++ switch(antcomb->second_quick_scan_conf) {
++ case ATH_ANT_DIV_COMB_LNA1:
+ antcomb->rssi_lna1 = alt_rssi_avg;
+- else if (antcomb->second_quick_scan_conf ==
+- ATH_ANT_DIV_COMB_LNA2)
++ break;
++ case ATH_ANT_DIV_COMB_LNA2:
+ antcomb->rssi_lna2 = alt_rssi_avg;
+- else if (antcomb->second_quick_scan_conf ==
+- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
++ break;
++ case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
+ if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
+ antcomb->rssi_lna2 = main_rssi_avg;
+ else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
+ antcomb->rssi_lna1 = main_rssi_avg;
++ break;
++ default:
++ break;
+ }
+
+ if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
+@@ -184,7 +338,7 @@ static void ath_select_ant_div_from_quic
+ div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+
+ if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
+- if (ath_is_alt_ant_ratio_better(alt_ratio,
++ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
+ ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+ ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+ main_rssi_avg, alt_rssi_avg,
+@@ -193,7 +347,7 @@ static void ath_select_ant_div_from_quic
+ else
+ antcomb->second_ratio = false;
+ } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
+- if (ath_is_alt_ant_ratio_better(alt_ratio,
++ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
+ ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
+ ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+ main_rssi_avg, alt_rssi_avg,
+@@ -202,105 +356,18 @@ static void ath_select_ant_div_from_quic
+ else
+ antcomb->second_ratio = false;
+ } else {
+- if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+- (alt_rssi_avg > main_rssi_avg +
+- ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
+- (alt_rssi_avg > main_rssi_avg)) &&
+- (antcomb->total_pkt_count > 50))
++ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
++ ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
++ 0,
++ main_rssi_avg, alt_rssi_avg,
++ antcomb->total_pkt_count))
+ antcomb->second_ratio = true;
+ else
+ antcomb->second_ratio = false;
+ }
+
+- /* set alt to the conf with maximun ratio */
+- if (antcomb->first_ratio && antcomb->second_ratio) {
+- if (antcomb->rssi_second > antcomb->rssi_third) {
+- /* first alt*/
+- if ((antcomb->first_quick_scan_conf ==
+- ATH_ANT_DIV_COMB_LNA1) ||
+- (antcomb->first_quick_scan_conf ==
+- ATH_ANT_DIV_COMB_LNA2))
+- /* Set alt LNA1 or LNA2*/
+- if (div_ant_conf->main_lna_conf ==
+- ATH_ANT_DIV_COMB_LNA2)
+- div_ant_conf->alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- else
+- div_ant_conf->alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- else
+- /* Set alt to A+B or A-B */
+- div_ant_conf->alt_lna_conf =
+- antcomb->first_quick_scan_conf;
+- } else if ((antcomb->second_quick_scan_conf ==
+- ATH_ANT_DIV_COMB_LNA1) ||
+- (antcomb->second_quick_scan_conf ==
+- ATH_ANT_DIV_COMB_LNA2)) {
+- /* Set alt LNA1 or LNA2 */
+- if (div_ant_conf->main_lna_conf ==
+- ATH_ANT_DIV_COMB_LNA2)
+- div_ant_conf->alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- else
+- div_ant_conf->alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- } else {
+- /* Set alt to A+B or A-B */
+- div_ant_conf->alt_lna_conf =
+- antcomb->second_quick_scan_conf;
+- }
+- } else if (antcomb->first_ratio) {
+- /* first alt */
+- if ((antcomb->first_quick_scan_conf ==
+- ATH_ANT_DIV_COMB_LNA1) ||
+- (antcomb->first_quick_scan_conf ==
+- ATH_ANT_DIV_COMB_LNA2))
+- /* Set alt LNA1 or LNA2 */
+- if (div_ant_conf->main_lna_conf ==
+- ATH_ANT_DIV_COMB_LNA2)
+- div_ant_conf->alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- else
+- div_ant_conf->alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- else
+- /* Set alt to A+B or A-B */
+- div_ant_conf->alt_lna_conf =
+- antcomb->first_quick_scan_conf;
+- } else if (antcomb->second_ratio) {
+- /* second alt */
+- if ((antcomb->second_quick_scan_conf ==
+- ATH_ANT_DIV_COMB_LNA1) ||
+- (antcomb->second_quick_scan_conf ==
+- ATH_ANT_DIV_COMB_LNA2))
+- /* Set alt LNA1 or LNA2 */
+- if (div_ant_conf->main_lna_conf ==
+- ATH_ANT_DIV_COMB_LNA2)
+- div_ant_conf->alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- else
+- div_ant_conf->alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- else
+- /* Set alt to A+B or A-B */
+- div_ant_conf->alt_lna_conf =
+- antcomb->second_quick_scan_conf;
+- } else {
+- /* main is largest */
+- if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
+- (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
+- /* Set alt LNA1 or LNA2 */
+- if (div_ant_conf->main_lna_conf ==
+- ATH_ANT_DIV_COMB_LNA2)
+- div_ant_conf->alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- else
+- div_ant_conf->alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- else
+- /* Set alt to A+B or A-B */
+- div_ant_conf->alt_lna_conf = antcomb->main_conf;
+- }
++ ath_ant_set_alt_ratio(antcomb, div_ant_conf);
++
+ break;
+ default:
+ break;
+@@ -430,8 +497,7 @@ static void ath_ant_div_conf_fast_divbia
+ ant_conf->fast_div_bias = 0x1;
+ break;
+ case 0x10: /* LNA2 A-B */
+- if (!(antcomb->scan) &&
+- (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
++ if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
+ ant_conf->fast_div_bias = 0x1;
+ else
+ ant_conf->fast_div_bias = 0x2;
+@@ -440,15 +506,13 @@ static void ath_ant_div_conf_fast_divbia
+ ant_conf->fast_div_bias = 0x1;
+ break;
+ case 0x13: /* LNA2 A+B */
+- if (!(antcomb->scan) &&
+- (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
++ if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
+ ant_conf->fast_div_bias = 0x1;
+ else
+ ant_conf->fast_div_bias = 0x2;
+ break;
+ case 0x20: /* LNA1 A-B */
+- if (!(antcomb->scan) &&
+- (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
++ if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
+ ant_conf->fast_div_bias = 0x1;
+ else
+ ant_conf->fast_div_bias = 0x2;
+@@ -457,8 +521,7 @@ static void ath_ant_div_conf_fast_divbia
+ ant_conf->fast_div_bias = 0x1;
+ break;
+ case 0x23: /* LNA1 A+B */
+- if (!(antcomb->scan) &&
+- (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
++ if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
+ ant_conf->fast_div_bias = 0x1;
+ else
+ ant_conf->fast_div_bias = 0x2;
+@@ -475,6 +538,9 @@ static void ath_ant_div_conf_fast_divbia
+ default:
+ break;
+ }
++
++ if (antcomb->fast_div_bias)
++ ant_conf->fast_div_bias = antcomb->fast_div_bias;
+ } else if (ant_conf->div_group == 3) {
+ switch ((ant_conf->main_lna_conf << 4) |
+ ant_conf->alt_lna_conf) {
+@@ -540,6 +606,138 @@ static void ath_ant_div_conf_fast_divbia
+ }
+ }
+
++static void ath_ant_try_scan(struct ath_ant_comb *antcomb,
++ struct ath_hw_antcomb_conf *conf,
++ int curr_alt_set, int alt_rssi_avg,
++ int main_rssi_avg)
++{
++ switch (curr_alt_set) {
++ case ATH_ANT_DIV_COMB_LNA2:
++ antcomb->rssi_lna2 = alt_rssi_avg;
++ antcomb->rssi_lna1 = main_rssi_avg;
++ antcomb->scan = true;
++ /* set to A+B */
++ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
++ break;
++ case ATH_ANT_DIV_COMB_LNA1:
++ antcomb->rssi_lna1 = alt_rssi_avg;
++ antcomb->rssi_lna2 = main_rssi_avg;
++ antcomb->scan = true;
++ /* set to A+B */
++ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
++ break;
++ case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
++ antcomb->rssi_add = alt_rssi_avg;
++ antcomb->scan = true;
++ /* set to A-B */
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
++ break;
++ case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
++ antcomb->rssi_sub = alt_rssi_avg;
++ antcomb->scan = false;
++ if (antcomb->rssi_lna2 >
++ (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
++ /* use LNA2 as main LNA */
++ if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
++ (antcomb->rssi_add > antcomb->rssi_sub)) {
++ /* set to A+B */
++ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
++ } else if (antcomb->rssi_sub >
++ antcomb->rssi_lna1) {
++ /* set to A-B */
++ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
++ } else {
++ /* set to LNA1 */
++ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ }
++ } else {
++ /* use LNA1 as main LNA */
++ if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
++ (antcomb->rssi_add > antcomb->rssi_sub)) {
++ /* set to A+B */
++ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
++ } else if (antcomb->rssi_sub >
++ antcomb->rssi_lna1) {
++ /* set to A-B */
++ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
++ } else {
++ /* set to LNA2 */
++ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ }
++ }
++ break;
++ default:
++ break;
++ }
++}
++
++static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf,
++ struct ath_ant_comb *antcomb,
++ int alt_ratio, int alt_rssi_avg,
++ int main_rssi_avg, int curr_main_set,
++ int curr_alt_set)
++{
++ bool ret = false;
++
++ if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio,
++ alt_rssi_avg, main_rssi_avg)) {
++ if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
++ /*
++ * Switch main and alt LNA.
++ */
++ div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
++ div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++ }
++
++ ret = true;
++ } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
++ (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
++ /*
++ Set alt to another LNA.
++ */
++ if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
++ div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
++ else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
++ div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
++
++ ret = true;
++ }
++
++ return ret;
++}
++
++static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb)
++{
++ int alt_ratio;
++
++ if (!antcomb->scan || !antcomb->alt_good)
++ return false;
++
++ if (time_after(jiffies, antcomb->scan_start_time +
++ msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
++ return true;
++
++ if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
++ alt_ratio = ((antcomb->alt_recv_cnt * 100) /
++ antcomb->total_pkt_count);
++ if (alt_ratio < antcomb->ant_ratio)
++ return true;
++ }
++
++ return false;
++}
++
+ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
+ {
+ struct ath_hw_antcomb_conf div_ant_conf;
+@@ -549,41 +747,46 @@ void ath_ant_comb_scan(struct ath_softc
+ int main_rssi = rs->rs_rssi_ctl0;
+ int alt_rssi = rs->rs_rssi_ctl1;
+ int rx_ant_conf, main_ant_conf;
+- bool short_scan = false;
++ bool short_scan = false, ret;
+
+ rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
+ ATH_ANT_RX_MASK;
+ main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
+ ATH_ANT_RX_MASK;
+
++ if (alt_rssi >= antcomb->low_rssi_thresh) {
++ antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO;
++ antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2;
++ } else {
++ antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI;
++ antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI;
++ }
++
+ /* Record packet only when both main_rssi and alt_rssi is positive */
+ if (main_rssi > 0 && alt_rssi > 0) {
+ antcomb->total_pkt_count++;
+ antcomb->main_total_rssi += main_rssi;
+ antcomb->alt_total_rssi += alt_rssi;
++
+ if (main_ant_conf == rx_ant_conf)
+ antcomb->main_recv_cnt++;
+ else
+ antcomb->alt_recv_cnt++;
+ }
+
+- /* Short scan check */
+- if (antcomb->scan && antcomb->alt_good) {
+- if (time_after(jiffies, antcomb->scan_start_time +
+- msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
+- short_scan = true;
+- else
+- if (antcomb->total_pkt_count ==
+- ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
+- alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+- antcomb->total_pkt_count);
+- if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
+- short_scan = true;
+- }
++ if (main_ant_conf == rx_ant_conf) {
++ ANT_STAT_INC(ANT_MAIN, recv_cnt);
++ ANT_LNA_INC(ANT_MAIN, rx_ant_conf);
++ } else {
++ ANT_STAT_INC(ANT_ALT, recv_cnt);
++ ANT_LNA_INC(ANT_ALT, rx_ant_conf);
+ }
+
++ /* Short scan check */
++ short_scan = ath_ant_short_scan_check(antcomb);
++
+ if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
+- rs->rs_moreaggr) && !short_scan)
++ rs->rs_moreaggr) && !short_scan)
+ return;
+
+ if (antcomb->total_pkt_count) {
+@@ -595,15 +798,13 @@ void ath_ant_comb_scan(struct ath_softc
+ antcomb->total_pkt_count);
+ }
+
+-
+ ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
+ curr_alt_set = div_ant_conf.alt_lna_conf;
+ curr_main_set = div_ant_conf.main_lna_conf;
+-
+ antcomb->count++;
+
+ if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
+- if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
++ if (alt_ratio > antcomb->ant_ratio) {
+ ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
+ main_rssi_avg);
+ antcomb->alt_good = true;
+@@ -617,153 +818,47 @@ void ath_ant_comb_scan(struct ath_softc
+ }
+
+ if (!antcomb->scan) {
+- if (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
+- alt_ratio, curr_main_set, curr_alt_set,
+- alt_rssi_avg, main_rssi_avg)) {
+- if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
+- /* Switch main and alt LNA */
+- div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
+- div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- }
+-
+- goto div_comb_done;
+- } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
+- (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
+- /* Set alt to another LNA */
+- if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+-
+- goto div_comb_done;
+- }
+-
+- if ((alt_rssi_avg < (main_rssi_avg +
+- div_ant_conf.lna1_lna2_delta)))
++ ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio,
++ alt_rssi_avg, main_rssi_avg,
++ curr_main_set, curr_alt_set);
++ if (ret)
+ goto div_comb_done;
+ }
+
++ if (!antcomb->scan &&
++ (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta)))
++ goto div_comb_done;
++
+ if (!antcomb->scan_not_start) {
+- switch (curr_alt_set) {
+- case ATH_ANT_DIV_COMB_LNA2:
+- antcomb->rssi_lna2 = alt_rssi_avg;
+- antcomb->rssi_lna1 = main_rssi_avg;
+- antcomb->scan = true;
+- /* set to A+B */
+- div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+- break;
+- case ATH_ANT_DIV_COMB_LNA1:
+- antcomb->rssi_lna1 = alt_rssi_avg;
+- antcomb->rssi_lna2 = main_rssi_avg;
+- antcomb->scan = true;
+- /* set to A+B */
+- div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+- break;
+- case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
+- antcomb->rssi_add = alt_rssi_avg;
+- antcomb->scan = true;
+- /* set to A-B */
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+- break;
+- case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
+- antcomb->rssi_sub = alt_rssi_avg;
+- antcomb->scan = false;
+- if (antcomb->rssi_lna2 >
+- (antcomb->rssi_lna1 +
+- ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
+- /* use LNA2 as main LNA */
+- if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
+- (antcomb->rssi_add > antcomb->rssi_sub)) {
+- /* set to A+B */
+- div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+- } else if (antcomb->rssi_sub >
+- antcomb->rssi_lna1) {
+- /* set to A-B */
+- div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+- } else {
+- /* set to LNA1 */
+- div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- }
+- } else {
+- /* use LNA1 as main LNA */
+- if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
+- (antcomb->rssi_add > antcomb->rssi_sub)) {
+- /* set to A+B */
+- div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+- } else if (antcomb->rssi_sub >
+- antcomb->rssi_lna1) {
+- /* set to A-B */
+- div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+- } else {
+- /* set to LNA2 */
+- div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
+- div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
+- }
+- }
+- break;
+- default:
+- break;
+- }
++ ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set,
++ alt_rssi_avg, main_rssi_avg);
+ } else {
+ if (!antcomb->alt_good) {
+ antcomb->scan_not_start = false;
+ /* Set alt to another LNA */
+ if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
+ div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
++ ATH_ANT_DIV_COMB_LNA2;
+ div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
++ ATH_ANT_DIV_COMB_LNA1;
+ } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
+ div_ant_conf.main_lna_conf =
+- ATH_ANT_DIV_COMB_LNA1;
++ ATH_ANT_DIV_COMB_LNA1;
+ div_ant_conf.alt_lna_conf =
+- ATH_ANT_DIV_COMB_LNA2;
++ ATH_ANT_DIV_COMB_LNA2;
+ }
+ goto div_comb_done;
+ }
++ ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
++ main_rssi_avg, alt_rssi_avg,
++ alt_ratio);
++ antcomb->quick_scan_cnt++;
+ }
+
+- ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
+- main_rssi_avg, alt_rssi_avg,
+- alt_ratio);
+-
+- antcomb->quick_scan_cnt++;
+-
+ div_comb_done:
+ ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
+ ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
++ ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg);
+
+ antcomb->scan_start_time = jiffies;
+ antcomb->total_pkt_count = 0;
+@@ -772,26 +867,3 @@ div_comb_done:
+ antcomb->main_recv_cnt = 0;
+ antcomb->alt_recv_cnt = 0;
+ }
+-
+-void ath_ant_comb_update(struct ath_softc *sc)
+-{
+- struct ath_hw *ah = sc->sc_ah;
+- struct ath_common *common = ath9k_hw_common(ah);
+- struct ath_hw_antcomb_conf div_ant_conf;
+- u8 lna_conf;
+-
+- ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
+-
+- if (sc->ant_rx == 1)
+- lna_conf = ATH_ANT_DIV_COMB_LNA1;
+- else
+- lna_conf = ATH_ANT_DIV_COMB_LNA2;
+-
+- div_ant_conf.main_lna_conf = lna_conf;
+- div_ant_conf.alt_lna_conf = lna_conf;
+-
+- ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
+-
+- if (common->antenna_diversity)
+- ath9k_hw_antctrl_shared_chain_lnadiv(ah, true);
+-}
+--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+@@ -610,7 +610,15 @@ static void ar5008_hw_override_ini(struc
+ REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
+
+ if (AR_SREV_9280_20_OR_LATER(ah)) {
+- val = REG_READ(ah, AR_PCU_MISC_MODE2);
++ /*
++ * For AR9280 and above, there is a new feature that allows
++ * Multicast search based on both MAC Address and Key ID.
++ * By default, this feature is enabled. But since the driver
++ * is not using this feature, we switch it off; otherwise
++ * multicast search based on MAC addr only will fail.
++ */
++ val = REG_READ(ah, AR_PCU_MISC_MODE2) &
++ (~AR_ADHOC_MCAST_KEYID_ENABLE);
+
+ if (!AR_SREV_9271(ah))
+ val &= ~AR_PCU_MISC_MODE2_HWWAR1;
+--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+@@ -555,6 +555,69 @@ static void ar9002_hw_antdiv_comb_conf_s
+ REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
+ }
+
++#ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
++
++static void ar9002_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
++{
++ struct ath_btcoex_hw *btcoex = &ah->btcoex_hw;
++ u8 antdiv_ctrl1, antdiv_ctrl2;
++ u32 regval;
++
++ if (enable) {
++ antdiv_ctrl1 = ATH_BT_COEX_ANTDIV_CONTROL1_ENABLE;
++ antdiv_ctrl2 = ATH_BT_COEX_ANTDIV_CONTROL2_ENABLE;
++
++ /*
++ * Don't disable BT ant to allow BB to control SWCOM.
++ */
++ btcoex->bt_coex_mode2 &= (~(AR_BT_DISABLE_BT_ANT));
++ REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2);
++
++ REG_WRITE(ah, AR_PHY_SWITCH_COM, ATH_BT_COEX_ANT_DIV_SWITCH_COM);
++ REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000);
++ } else {
++ /*
++ * Disable antenna diversity, use LNA1 only.
++ */
++ antdiv_ctrl1 = ATH_BT_COEX_ANTDIV_CONTROL1_FIXED_A;
++ antdiv_ctrl2 = ATH_BT_COEX_ANTDIV_CONTROL2_FIXED_A;
++
++ /*
++ * Disable BT Ant. to allow concurrent BT and WLAN receive.
++ */
++ btcoex->bt_coex_mode2 |= AR_BT_DISABLE_BT_ANT;
++ REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2);
++
++ /*
++ * Program SWCOM table to make sure RF switch always parks
++ * at BT side.
++ */
++ REG_WRITE(ah, AR_PHY_SWITCH_COM, 0);
++ REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000);
++ }
++
++ regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
++ regval &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL));
++ /*
++ * Clear ant_fast_div_bias [14:9] since for WB195,
++ * the main LNA is always LNA1.
++ */
++ regval &= (~(AR_PHY_9285_FAST_DIV_BIAS));
++ regval |= SM(antdiv_ctrl1, AR_PHY_9285_ANT_DIV_CTL);
++ regval |= SM(antdiv_ctrl2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
++ regval |= SM((antdiv_ctrl2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
++ regval |= SM((antdiv_ctrl1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB);
++ regval |= SM((antdiv_ctrl1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
++ REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
++
++ regval = REG_READ(ah, AR_PHY_CCK_DETECT);
++ regval &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
++ regval |= SM((antdiv_ctrl1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
++ REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
++}
++
++#endif
++
+ static void ar9002_hw_spectral_scan_config(struct ath_hw *ah,
+ struct ath_spec_scan *param)
+ {
+@@ -634,5 +697,9 @@ void ar9002_hw_attach_phy_ops(struct ath
+ ops->spectral_scan_trigger = ar9002_hw_spectral_scan_trigger;
+ ops->spectral_scan_wait = ar9002_hw_spectral_scan_wait;
+
++#ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
++ ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity;
++#endif
++
+ ar9002_hw_set_nf_limits(ah);
+ }
+--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h
++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
+@@ -317,13 +317,15 @@
+ #define AR_PHY_9285_ANT_DIV_ALT_GAINTB_S 29
+ #define AR_PHY_9285_ANT_DIV_MAIN_GAINTB 0x40000000
+ #define AR_PHY_9285_ANT_DIV_MAIN_GAINTB_S 30
+-#define AR_PHY_9285_ANT_DIV_LNA1 2
+-#define AR_PHY_9285_ANT_DIV_LNA2 1
+-#define AR_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2 3
+-#define AR_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2 0
+ #define AR_PHY_9285_ANT_DIV_GAINTB_0 0
+ #define AR_PHY_9285_ANT_DIV_GAINTB_1 1
+
++#define ATH_BT_COEX_ANTDIV_CONTROL1_ENABLE 0x0b
++#define ATH_BT_COEX_ANTDIV_CONTROL2_ENABLE 0x09
++#define ATH_BT_COEX_ANTDIV_CONTROL1_FIXED_A 0x04
++#define ATH_BT_COEX_ANTDIV_CONTROL2_FIXED_A 0x09
++#define ATH_BT_COEX_ANT_DIV_SWITCH_COM 0x66666666
++
+ #define AR_PHY_EXT_CCA0 0x99b8
+ #define AR_PHY_EXT_CCA0_THRESH62 0x000000FF
+ #define AR_PHY_EXT_CCA0_THRESH62_S 0
+--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+@@ -3541,13 +3541,12 @@ static u16 ar9003_switch_com_spdt_get(st
+ return le16_to_cpu(ar9003_modal_header(ah, is2ghz)->switchcomspdt);
+ }
+
+-
+-static u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz)
++u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz)
+ {
+ return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon);
+ }
+
+-static u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz)
++u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz)
+ {
+ return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon2);
+ }
+@@ -3561,6 +3560,7 @@ static u16 ar9003_hw_ant_ctrl_chain_get(
+
+ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
+ {
++ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
+ int chain;
+ u32 regval, value, gpio;
+@@ -3614,6 +3614,11 @@ static void ar9003_hw_ant_ctrl_apply(str
+ }
+
+ value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz);
++ if (AR_SREV_9485(ah) && common->bt_ant_diversity) {
++ regval &= ~AR_SWITCH_TABLE_COM2_ALL;
++ regval |= ah->config.ant_ctrl_comm2g_switch_enable;
++
++ }
+ REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value);
+
+ if ((AR_SREV_9462(ah)) && (ah->rxchainmask == 0x2)) {
+@@ -3645,8 +3650,11 @@ static void ar9003_hw_ant_ctrl_apply(str
+ regval &= (~AR_PHY_ANT_DIV_LNADIV);
+ regval |= ((value >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
+
++ if (AR_SREV_9485(ah) && common->bt_ant_diversity)
++ regval |= AR_ANT_DIV_ENABLE;
++
+ if (AR_SREV_9565(ah)) {
+- if (ah->shared_chain_lnadiv) {
++ if (common->bt_ant_diversity) {
+ regval |= (1 << AR_PHY_ANT_SW_RX_PROT_S);
+ } else {
+ regval &= ~(1 << AR_PHY_ANT_DIV_LNADIV_S);
+@@ -3656,10 +3664,14 @@ static void ar9003_hw_ant_ctrl_apply(str
+
+ REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+
+- /*enable fast_div */
++ /* enable fast_div */
+ regval = REG_READ(ah, AR_PHY_CCK_DETECT);
+ regval &= (~AR_FAST_DIV_ENABLE);
+ regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
++
++ if (AR_SREV_9485(ah) && common->bt_ant_diversity)
++ regval |= AR_FAST_DIV_ENABLE;
++
+ REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
+
+ if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
+@@ -3673,9 +3685,9 @@ static void ar9003_hw_ant_ctrl_apply(str
+ AR_PHY_ANT_DIV_ALT_GAINTB |
+ AR_PHY_ANT_DIV_MAIN_GAINTB));
+ /* by default use LNA1 for the main antenna */
+- regval |= (AR_PHY_ANT_DIV_LNA1 <<
++ regval |= (ATH_ANT_DIV_COMB_LNA1 <<
+ AR_PHY_ANT_DIV_MAIN_LNACONF_S);
+- regval |= (AR_PHY_ANT_DIV_LNA2 <<
++ regval |= (ATH_ANT_DIV_COMB_LNA2 <<
+ AR_PHY_ANT_DIV_ALT_LNACONF_S);
+ REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+ }
+@@ -3813,6 +3825,11 @@ static void ar9003_hw_atten_apply(struct
+ else
+ value = ar9003_hw_atten_chain_get_margin(ah, i, chan);
+
++ if (ah->config.alt_mingainidx)
++ REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_0,
++ AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN,
++ value);
++
+ REG_RMW_FIELD(ah, ext_atten_reg[i],
+ AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN,
+ value);
+--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
++++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
+@@ -334,6 +334,8 @@ struct ar9300_eeprom {
+
+ s32 ar9003_hw_get_tx_gain_idx(struct ath_hw *ah);
+ s32 ar9003_hw_get_rx_gain_idx(struct ath_hw *ah);
++u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz);
++u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz);
+
+ u8 *ar9003_get_spur_chan_ptr(struct ath_hw *ah, bool is_2ghz);
+
+--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+@@ -148,6 +148,8 @@
+ #define AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S 28
+ #define AR_PHY_EXT_CCA_THRESH62 0x007F0000
+ #define AR_PHY_EXT_CCA_THRESH62_S 16
++#define AR_PHY_EXTCHN_PWRTHR1_ANT_DIV_ALT_ANT_MINGAINIDX 0x0000FF00
++#define AR_PHY_EXTCHN_PWRTHR1_ANT_DIV_ALT_ANT_MINGAINIDX_S 8
+ #define AR_PHY_EXT_MINCCA_PWR 0x01FF0000
+ #define AR_PHY_EXT_MINCCA_PWR_S 16
+ #define AR_PHY_EXT_CYCPWR_THR1 0x0000FE00L
+@@ -296,11 +298,6 @@
+ #define AR_PHY_ANT_DIV_MAIN_GAINTB 0x40000000
+ #define AR_PHY_ANT_DIV_MAIN_GAINTB_S 30
+
+-#define AR_PHY_ANT_DIV_LNA1_MINUS_LNA2 0x0
+-#define AR_PHY_ANT_DIV_LNA2 0x1
+-#define AR_PHY_ANT_DIV_LNA1 0x2
+-#define AR_PHY_ANT_DIV_LNA1_PLUS_LNA2 0x3
+-
+ #define AR_PHY_EXTCHN_PWRTHR1 (AR_AGC_BASE + 0x2c)
+ #define AR_PHY_EXT_CHN_WIN (AR_AGC_BASE + 0x30)
+ #define AR_PHY_20_40_DET_THR (AR_AGC_BASE + 0x34)
+--- a/drivers/net/wireless/ath/ath9k/debug.h
++++ b/drivers/net/wireless/ath/ath9k/debug.h
+@@ -28,9 +28,13 @@ struct fft_sample_tlv;
+ #ifdef CPTCFG_ATH9K_DEBUGFS
+ #define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++
+ #define RESET_STAT_INC(sc, type) sc->debug.stats.reset[type]++
++#define ANT_STAT_INC(i, c) sc->debug.stats.ant_stats[i].c++
++#define ANT_LNA_INC(i, c) sc->debug.stats.ant_stats[i].lna_recv_cnt[c]++;
+ #else
+ #define TX_STAT_INC(q, c) do { } while (0)
+ #define RESET_STAT_INC(sc, type) do { } while (0)
++#define ANT_STAT_INC(i, c) do { } while (0)
++#define ANT_LNA_INC(i, c) do { } while (0)
+ #endif
+
+ enum ath_reset_type {
+@@ -243,11 +247,22 @@ struct ath_rx_stats {
+ u32 rx_spectral;
+ };
+
++#define ANT_MAIN 0
++#define ANT_ALT 1
++
++struct ath_antenna_stats {
++ u32 recv_cnt;
++ u32 rssi_avg;
++ u32 lna_recv_cnt[4];
++ u32 lna_attempt_cnt[4];
++};
++
+ struct ath_stats {
+ struct ath_interrupt_stats istats;
+ struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES];
+ struct ath_rx_stats rxstats;
+ struct ath_dfs_stats dfs_stats;
++ struct ath_antenna_stats ant_stats[2];
+ u32 reset[__RESET_TYPE_MAX];
+ };
+
+@@ -281,10 +296,11 @@ void ath9k_sta_remove_debugfs(struct iee
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct dentry *dir);
+-
+ void ath_debug_send_fft_sample(struct ath_softc *sc,
+ struct fft_sample_tlv *fft_sample);
+-
++void ath9k_debug_stat_ant(struct ath_softc *sc,
++ struct ath_hw_antcomb_conf *div_ant_conf,
++ int main_rssi_avg, int alt_rssi_avg);
+ #else
+
+ #define RX_STAT_INC(c) /* NOP */
+@@ -297,12 +313,10 @@ static inline int ath9k_init_debug(struc
+ static inline void ath9k_deinit_debug(struct ath_softc *sc)
+ {
+ }
+-
+ static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
+ enum ath9k_int status)
+ {
+ }
+-
+ static inline void ath_debug_stat_tx(struct ath_softc *sc,
+ struct ath_buf *bf,
+ struct ath_tx_status *ts,
+@@ -310,11 +324,16 @@ static inline void ath_debug_stat_tx(str
+ unsigned int flags)
+ {
+ }
+-
+ static inline void ath_debug_stat_rx(struct ath_softc *sc,
+ struct ath_rx_status *rs)
+ {
+ }
++static inline void ath9k_debug_stat_ant(struct ath_softc *sc,
++ struct ath_hw_antcomb_conf *div_ant_conf,
++ int main_rssi_avg, int alt_rssi_avg)
++{
++
++}
+
+ #endif /* CPTCFG_ATH9K_DEBUGFS */
+
+--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
++++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+@@ -812,6 +812,7 @@ static void ath9k_hw_4k_set_gain(struct
+ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
+ struct ath9k_channel *chan)
+ {
++ struct ath9k_hw_capabilities *pCap = &ah->caps;
+ struct modal_eep_4k_header *pModal;
+ struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
+ struct base_eep_header_4k *pBase = &eep->baseEepHeader;
+@@ -858,6 +859,24 @@ static void ath9k_hw_4k_set_board_values
+
+ REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal);
+ regVal = REG_READ(ah, AR_PHY_CCK_DETECT);
++
++ if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
++ /*
++ * If diversity combining is enabled,
++ * set MAIN to LNA1 and ALT to LNA2 initially.
++ */
++ regVal = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
++ regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF |
++ AR_PHY_9285_ANT_DIV_ALT_LNACONF));
++
++ regVal |= (ATH_ANT_DIV_COMB_LNA1 <<
++ AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S);
++ regVal |= (ATH_ANT_DIV_COMB_LNA2 <<
++ AR_PHY_9285_ANT_DIV_ALT_LNACONF_S);
++ regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS));
++ regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S);
++ REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
++ }
+ }
+
+ if (pModal->version >= 2) {
+--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
++++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
+@@ -78,13 +78,16 @@ static inline void ath9k_hw_antdiv_comb_
+ ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf);
+ }
+
+-static inline void ath9k_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah,
+- bool enable)
++#ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
++
++static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
+ {
+- if (ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv)
+- ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv(ah, enable);
++ if (ath9k_hw_ops(ah)->set_bt_ant_diversity)
++ ath9k_hw_ops(ah)->set_bt_ant_diversity(ah, enable);
+ }
+
++#endif
++
+ /* Private hardware call ops */
+
+ /* PHY ops */
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -450,7 +450,6 @@ static void ath9k_hw_init_config(struct
+ ah->config.ack_6mb = 0x0;
+ ah->config.cwm_ignore_extcca = 0;
+ ah->config.pcie_clock_req = 0;
+- ah->config.pcie_waen = 0;
+ ah->config.analog_shiftreg = 1;
+
+ for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
+@@ -1069,7 +1068,7 @@ void ath9k_hw_init_global_settings(struc
+ if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+ tx_lat += 11;
+
+- sifstime *= 2;
++ sifstime = 32;
+ ack_offset = 16;
+ slottime = 13;
+ } else if (IS_CHAN_QUARTER_RATE(chan)) {
+@@ -1079,7 +1078,7 @@ void ath9k_hw_init_global_settings(struc
+ if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+ tx_lat += 22;
+
+- sifstime *= 4;
++ sifstime = 64;
+ ack_offset = 32;
+ slottime = 21;
+ } else {
+@@ -1116,7 +1115,6 @@ void ath9k_hw_init_global_settings(struc
+ ctstimeout += 48 - sifstime - ah->slottime;
+ }
+
+-
+ ath9k_hw_set_sifs_time(ah, sifstime);
+ ath9k_hw_setslottime(ah, slottime);
+ ath9k_hw_set_ack_timeout(ah, acktimeout);
+@@ -1496,16 +1494,18 @@ static bool ath9k_hw_channel_change(stru
+ struct ath9k_channel *chan)
+ {
+ struct ath_common *common = ath9k_hw_common(ah);
++ struct ath9k_hw_capabilities *pCap = &ah->caps;
++ bool band_switch = false, mode_diff = false;
++ u8 ini_reloaded = 0;
+ u32 qnum;
+ int r;
+- bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
+- bool band_switch, mode_diff;
+- u8 ini_reloaded;
+-
+- band_switch = (chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)) !=
+- (ah->curchan->channelFlags & (CHANNEL_2GHZ |
+- CHANNEL_5GHZ));
+- mode_diff = (chan->chanmode != ah->curchan->chanmode);
++
++ if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) {
++ u32 cur = ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
++ u32 new = chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
++ band_switch = (cur != new);
++ mode_diff = (chan->chanmode != ah->curchan->chanmode);
++ }
+
+ for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
+ if (ath9k_hw_numtxpending(ah, qnum)) {
+@@ -1520,11 +1520,12 @@ static bool ath9k_hw_channel_change(stru
+ return false;
+ }
+
+- if (edma && (band_switch || mode_diff)) {
++ if (band_switch || mode_diff) {
+ ath9k_hw_mark_phy_inactive(ah);
+ udelay(5);
+
+- ath9k_hw_init_pll(ah, NULL);
++ if (band_switch)
++ ath9k_hw_init_pll(ah, chan);
+
+ if (ath9k_hw_fast_chan_change(ah, chan, &ini_reloaded)) {
+ ath_err(common, "Failed to do fast channel change\n");
+@@ -1541,22 +1542,21 @@ static bool ath9k_hw_channel_change(stru
+ }
+ ath9k_hw_set_clockrate(ah);
+ ath9k_hw_apply_txpower(ah, chan, false);
+- ath9k_hw_rfbus_done(ah);
+
+ if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
+ ath9k_hw_set_delta_slope(ah, chan);
+
+ ath9k_hw_spur_mitigate_freq(ah, chan);
+
+- if (edma && (band_switch || mode_diff)) {
+- ah->ah_flags |= AH_FASTCC;
+- if (band_switch || ini_reloaded)
+- ah->eep_ops->set_board_values(ah, chan);
++ if (band_switch || ini_reloaded)
++ ah->eep_ops->set_board_values(ah, chan);
+
+- ath9k_hw_init_bb(ah, chan);
++ ath9k_hw_init_bb(ah, chan);
++ ath9k_hw_rfbus_done(ah);
+
+- if (band_switch || ini_reloaded)
+- ath9k_hw_init_cal(ah, chan);
++ if (band_switch || ini_reloaded) {
++ ah->ah_flags |= AH_FASTCC;
++ ath9k_hw_init_cal(ah, chan);
+ ah->ah_flags &= ~AH_FASTCC;
+ }
+
+@@ -1778,16 +1778,11 @@ static void ath9k_hw_init_desc(struct at
+ /*
+ * Fast channel change:
+ * (Change synthesizer based on channel freq without resetting chip)
+- *
+- * Don't do FCC when
+- * - Flag is not set
+- * - Chip is just coming out of full sleep
+- * - Channel to be set is same as current channel
+- * - Channel flags are different, (eg.,moving from 2GHz to 5GHz channel)
+ */
+ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
+ {
+ struct ath_common *common = ath9k_hw_common(ah);
++ struct ath9k_hw_capabilities *pCap = &ah->caps;
+ int ret;
+
+ if (AR_SREV_9280(ah) && common->bus_ops->ath_bus_type == ATH_PCI)
+@@ -1806,9 +1801,21 @@ static int ath9k_hw_do_fastcc(struct ath
+ (CHANNEL_HALF | CHANNEL_QUARTER))
+ goto fail;
+
+- if ((chan->channelFlags & CHANNEL_ALL) !=
+- (ah->curchan->channelFlags & CHANNEL_ALL))
+- goto fail;
++ /*
++ * If cross-band fcc is not supoprted, bail out if
++ * either channelFlags or chanmode differ.
++ *
++ * chanmode will be different if the HT operating mode
++ * changes because of CSA.
++ */
++ if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH)) {
++ if ((chan->channelFlags & CHANNEL_ALL) !=
++ (ah->curchan->channelFlags & CHANNEL_ALL))
++ goto fail;
++
++ if (chan->chanmode != ah->curchan->chanmode)
++ goto fail;
++ }
+
+ if (!ath9k_hw_check_alive(ah))
+ goto fail;
+@@ -2047,7 +2054,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st
+
+ ath9k_hw_apply_gpio_override(ah);
+
+- if (AR_SREV_9565(ah) && ah->shared_chain_lnadiv)
++ if (AR_SREV_9565(ah) && common->bt_ant_diversity)
+ REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
+
+ return 0;
+@@ -2550,34 +2557,28 @@ int ath9k_hw_fill_cap_info(struct ath_hw
+ if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah))
+ pCap->hw_caps |= ATH9K_HW_CAP_SGI_20;
+
+- if (AR_SREV_9285(ah))
++ if (AR_SREV_9285(ah)) {
+ if (ah->eep_ops->get_eeprom(ah, EEP_MODAL_VER) >= 3) {
+ ant_div_ctl1 =
+ ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
+- if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1))
++ if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1)) {
+ pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
++ ath_info(common, "Enable LNA combining\n");
++ }
+ }
++ }
++
+ if (AR_SREV_9300_20_OR_LATER(ah)) {
+ if (ah->eep_ops->get_eeprom(ah, EEP_CHAIN_MASK_REDUCE))
+ pCap->hw_caps |= ATH9K_HW_CAP_APM;
+ }
+
+-
+ if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) {
+ ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
+- /*
+- * enable the diversity-combining algorithm only when
+- * both enable_lna_div and enable_fast_div are set
+- * Table for Diversity
+- * ant_div_alt_lnaconf bit 0-1
+- * ant_div_main_lnaconf bit 2-3
+- * ant_div_alt_gaintb bit 4
+- * ant_div_main_gaintb bit 5
+- * enable_ant_div_lnadiv bit 6
+- * enable_ant_fast_div bit 7
+- */
+- if ((ant_div_ctl1 >> 0x6) == 0x3)
++ if ((ant_div_ctl1 >> 0x6) == 0x3) {
+ pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
++ ath_info(common, "Enable LNA combining\n");
++ }
+ }
+
+ if (ath9k_hw_dfs_tested(ah))
+@@ -2610,6 +2611,13 @@ int ath9k_hw_fill_cap_info(struct ath_hw
+ ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
+ pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
+
++ /*
++ * Fast channel change across bands is available
++ * only for AR9462 and AR9565.
++ */
++ if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
++ pCap->hw_caps |= ATH9K_HW_CAP_FCC_BAND_SWITCH;
++
+ return 0;
+ }
+
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -247,6 +247,8 @@ enum ath9k_hw_caps {
+ ATH9K_HW_CAP_DFS = BIT(16),
+ ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17),
+ ATH9K_HW_CAP_PAPRD = BIT(18),
++ ATH9K_HW_CAP_FCC_BAND_SWITCH = BIT(19),
++ ATH9K_HW_CAP_BT_ANT_DIV = BIT(20),
+ };
+
+ /*
+@@ -309,8 +311,11 @@ struct ath9k_ops_config {
+ u16 ani_poll_interval; /* ANI poll interval in ms */
+
+ /* Platform specific config */
++ u32 aspm_l1_fix;
+ u32 xlna_gpio;
++ u32 ant_ctrl_comm2g_switch_enable;
+ bool xatten_margin_cfg;
++ bool alt_mingainidx;
+ };
+
+ enum ath9k_int {
+@@ -716,11 +721,14 @@ struct ath_hw_ops {
+ struct ath_hw_antcomb_conf *antconf);
+ void (*antdiv_comb_conf_set)(struct ath_hw *ah,
+ struct ath_hw_antcomb_conf *antconf);
+- void (*antctrl_shared_chain_lnadiv)(struct ath_hw *hw, bool enable);
+ void (*spectral_scan_config)(struct ath_hw *ah,
+ struct ath_spec_scan *param);
+ void (*spectral_scan_trigger)(struct ath_hw *ah);
+ void (*spectral_scan_wait)(struct ath_hw *ah);
++
++#ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
++ void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable);
++#endif
+ };
+
+ struct ath_nf_limits {
+@@ -765,7 +773,6 @@ struct ath_hw {
+ bool aspm_enabled;
+ bool is_monitoring;
+ bool need_an_top2_fixup;
+- bool shared_chain_lnadiv;
+ u16 tx_trig_level;
+
+ u32 nf_regs[6];
+--- a/drivers/net/wireless/ath/ath9k/pci.c
++++ b/drivers/net/wireless/ath/ath9k/pci.c
+@@ -29,6 +29,60 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_i
+ { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */
+ { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
+ { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
++
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x002A,
++ PCI_VENDOR_ID_AZWAVE,
++ 0x1C71),
++ .driver_data = ATH9K_PCI_D3_L1_WAR },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x002A,
++ PCI_VENDOR_ID_FOXCONN,
++ 0xE01F),
++ .driver_data = ATH9K_PCI_D3_L1_WAR },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x002A,
++ 0x11AD, /* LITEON */
++ 0x6632),
++ .driver_data = ATH9K_PCI_D3_L1_WAR },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x002A,
++ 0x11AD, /* LITEON */
++ 0x6642),
++ .driver_data = ATH9K_PCI_D3_L1_WAR },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x002A,
++ PCI_VENDOR_ID_QMI,
++ 0x0306),
++ .driver_data = ATH9K_PCI_D3_L1_WAR },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x002A,
++ 0x185F, /* WNC */
++ 0x309D),
++ .driver_data = ATH9K_PCI_D3_L1_WAR },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x002A,
++ 0x10CF, /* Fujitsu */
++ 0x147C),
++ .driver_data = ATH9K_PCI_D3_L1_WAR },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x002A,
++ 0x10CF, /* Fujitsu */
++ 0x147D),
++ .driver_data = ATH9K_PCI_D3_L1_WAR },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x002A,
++ 0x10CF, /* Fujitsu */
++ 0x1536),
++ .driver_data = ATH9K_PCI_D3_L1_WAR },
++
++ /* AR9285 card for Asus */
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x002B,
++ PCI_VENDOR_ID_AZWAVE,
++ 0x2C37),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++
+ { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
+ { PCI_VDEVICE(ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */
+ { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI */
+@@ -40,29 +94,106 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_i
+ 0x0032,
+ PCI_VENDOR_ID_AZWAVE,
+ 0x2086),
+- .driver_data = ATH9K_PCI_CUS198 },
++ .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_AZWAVE,
+ 0x1237),
+- .driver_data = ATH9K_PCI_CUS198 },
++ .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_AZWAVE,
+ 0x2126),
+- .driver_data = ATH9K_PCI_CUS198 },
++ .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_AZWAVE,
++ 0x126A),
++ .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
+
+ /* PCI-E CUS230 */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_AZWAVE,
+ 0x2152),
+- .driver_data = ATH9K_PCI_CUS230 },
++ .driver_data = ATH9K_PCI_CUS230 | ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_FOXCONN,
+ 0xE075),
+- .driver_data = ATH9K_PCI_CUS230 },
++ .driver_data = ATH9K_PCI_CUS230 | ATH9K_PCI_BT_ANT_DIV },
++
++ /* WB225 */
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_ATHEROS,
++ 0x3119),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_ATHEROS,
++ 0x3122),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ 0x185F, /* WNC */
++ 0x3119),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ 0x185F, /* WNC */
++ 0x3027),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_SAMSUNG,
++ 0x4105),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_SAMSUNG,
++ 0x4106),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_SAMSUNG,
++ 0x410D),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_SAMSUNG,
++ 0x410E),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_SAMSUNG,
++ 0x410F),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_SAMSUNG,
++ 0xC706),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_SAMSUNG,
++ 0xC680),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_SAMSUNG,
++ 0xC708),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_LENOVO,
++ 0x3218),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
++ 0x0032,
++ PCI_VENDOR_ID_LENOVO,
++ 0x3219),
++ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+
+ { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */
+ { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */
+@@ -229,6 +360,22 @@ static void ath_pci_aspm_init(struct ath
+ return;
+ }
+
++ /*
++ * 0x70c - Ack Frequency Register.
++ *
++ * Bits 27:29 - DEFAULT_L1_ENTRANCE_LATENCY.
++ *
++ * 000 : 1 us
++ * 001 : 2 us
++ * 010 : 4 us
++ * 011 : 8 us
++ * 100 : 16 us
++ * 101 : 32 us
++ * 110/111 : 64 us
++ */
++ if (AR_SREV_9462(ah))
++ pci_read_config_dword(pdev, 0x70c, &ah->config.aspm_l1_fix);
++
+ pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm);
+ if (aspm & (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1)) {
+ ah->aspm_enabled = true;
+--- a/drivers/net/wireless/ath/ath9k/phy.h
++++ b/drivers/net/wireless/ath/ath9k/phy.h
+@@ -48,4 +48,11 @@
+ #define AR_PHY_PLL_CONTROL 0x16180
+ #define AR_PHY_PLL_MODE 0x16184
+
++enum ath9k_ant_div_comb_lna_conf {
++ ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
++ ATH_ANT_DIV_COMB_LNA2,
++ ATH_ANT_DIV_COMB_LNA1,
++ ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2,
++};
++
+ #endif
+--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
++++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
+@@ -73,7 +73,6 @@
+ #include "iwl-prph.h"
+
+ /* A TimeUnit is 1024 microsecond */
+-#define TU_TO_JIFFIES(_tu) (usecs_to_jiffies((_tu) * 1024))
+ #define MSEC_TO_TU(_msec) (_msec*1000/1024)
+
+ /*
+@@ -191,8 +190,7 @@ static void iwl_mvm_te_handle_notif(stru
+ iwl_mvm_te_clear_data(mvm, te_data);
+ } else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) {
+ te_data->running = true;
+- te_data->end_jiffies = jiffies +
+- TU_TO_JIFFIES(te_data->duration);
++ te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration);
+
+ if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+@@ -329,8 +327,7 @@ void iwl_mvm_protect_session(struct iwl_
+ lockdep_assert_held(&mvm->mutex);
+
+ if (te_data->running &&
+- time_after(te_data->end_jiffies,
+- jiffies + TU_TO_JIFFIES(min_duration))) {
++ time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
+ IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
+ jiffies_to_msecs(te_data->end_jiffies - jiffies));
+ return;
+--- a/include/linux/ieee80211.h
++++ b/include/linux/ieee80211.h
+@@ -2279,4 +2279,8 @@ static inline bool ieee80211_check_tim(c
+ return !!(tim->virtual_map[index] & mask);
+ }
+
++/* convert time units */
++#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
++#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
++
+ #endif /* LINUX_IEEE80211_H */
+--- a/net/mac80211/rate.c
++++ b/net/mac80211/rate.c
+@@ -210,7 +210,7 @@ static bool rc_no_data_or_no_ack_use_min
+ !ieee80211_is_data(fc);
+ }
+
+-static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
++static void rc_send_low_basicrate(s8 *idx, u32 basic_rates,
+ struct ieee80211_supported_band *sband)
+ {
+ u8 i;
+@@ -272,28 +272,37 @@ static void __rate_control_send_low(stru
+ }
+
+
+-bool rate_control_send_low(struct ieee80211_sta *sta,
++bool rate_control_send_low(struct ieee80211_sta *pubsta,
+ void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc)
+ {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
+ struct ieee80211_supported_band *sband = txrc->sband;
++ struct sta_info *sta;
+ int mcast_rate;
++ bool use_basicrate = false;
+
+- if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
+- __rate_control_send_low(txrc->hw, sband, sta, info);
++ if (!pubsta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
++ __rate_control_send_low(txrc->hw, sband, pubsta, info);
+
+- if (!sta && txrc->bss) {
++ if (!pubsta && txrc->bss) {
+ mcast_rate = txrc->bss_conf->mcast_rate[sband->band];
+ if (mcast_rate > 0) {
+ info->control.rates[0].idx = mcast_rate - 1;
+ return true;
+ }
++ use_basicrate = true;
++ } else if (pubsta) {
++ sta = container_of(pubsta, struct sta_info, sta);
++ if (ieee80211_vif_is_mesh(&sta->sdata->vif))
++ use_basicrate = true;
++ }
+
+- rc_send_low_broadcast(&info->control.rates[0].idx,
++ if (use_basicrate)
++ rc_send_low_basicrate(&info->control.rates[0].idx,
+ txrc->bss_conf->basic_rates,
+ sband);
+- }
++
+ return true;
+ }
+ return false;
+--- a/drivers/net/wireless/ath/ath9k/Kconfig
++++ b/drivers/net/wireless/ath/ath9k/Kconfig
+@@ -60,7 +60,7 @@ config ATH9K_AHB
+
+ config ATH9K_DEBUGFS
+ bool "Atheros ath9k debugging"
+- depends on ATH9K
++ depends on ATH9K && DEBUG_FS
+ select MAC80211_DEBUGFS
+ depends on RELAY
+ ---help---
+--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
++++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+@@ -269,13 +269,12 @@ static void ar9002_hw_configpcipowersave
+ if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE)
+ val |= AR_WA_D3_L1_DISABLE;
+ } else {
+- if (((AR_SREV_9285(ah) ||
+- AR_SREV_9271(ah) ||
+- AR_SREV_9287(ah)) &&
+- (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)) ||
+- (AR_SREV_9280(ah) &&
+- (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE))) {
+- val |= AR_WA_D3_L1_DISABLE;
++ if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) {
++ if (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)
++ val |= AR_WA_D3_L1_DISABLE;
++ } else if (AR_SREV_9280(ah)) {
++ if (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE)
++ val |= AR_WA_D3_L1_DISABLE;
+ }
+ }
+
+@@ -297,24 +296,18 @@ static void ar9002_hw_configpcipowersave
+ } else {
+ if (ah->config.pcie_waen) {
+ val = ah->config.pcie_waen;
+- if (!power_off)
+- val &= (~AR_WA_D3_L1_DISABLE);
++ val &= (~AR_WA_D3_L1_DISABLE);
+ } else {
+- if (AR_SREV_9285(ah) ||
+- AR_SREV_9271(ah) ||
+- AR_SREV_9287(ah)) {
++ if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) {
+ val = AR9285_WA_DEFAULT;
+- if (!power_off)
+- val &= (~AR_WA_D3_L1_DISABLE);
+- }
+- else if (AR_SREV_9280(ah)) {
++ val &= (~AR_WA_D3_L1_DISABLE);
++ } else if (AR_SREV_9280(ah)) {
+ /*
+ * For AR9280 chips, bit 22 of 0x4004
+ * needs to be set.
+ */
+ val = AR9280_WA_DEFAULT;
+- if (!power_off)
+- val &= (~AR_WA_D3_L1_DISABLE);
++ val &= (~AR_WA_D3_L1_DISABLE);
+ } else {
+ val = AR_WA_DEFAULT;
+ }
+--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+@@ -153,7 +153,7 @@ static void ar9003_hw_init_mode_regs(str
+ if (!ah->is_clk_25mhz)
+ INIT_INI_ARRAY(&ah->iniAdditional,
+ ar9340_1p0_radio_core_40M);
+- } else if (AR_SREV_9485_11(ah)) {
++ } else if (AR_SREV_9485_11_OR_LATER(ah)) {
+ /* mac */
+ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
+ ar9485_1_1_mac_core);
+@@ -424,7 +424,7 @@ static void ar9003_tx_gain_table_mode0(s
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9340Modes_lowest_ob_db_tx_gain_table_1p0);
+- else if (AR_SREV_9485_11(ah))
++ else if (AR_SREV_9485_11_OR_LATER(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9485_modes_lowest_ob_db_tx_gain_1_1);
+ else if (AR_SREV_9550(ah))
+@@ -458,7 +458,7 @@ static void ar9003_tx_gain_table_mode1(s
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9340Modes_high_ob_db_tx_gain_table_1p0);
+- else if (AR_SREV_9485_11(ah))
++ else if (AR_SREV_9485_11_OR_LATER(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9485Modes_high_ob_db_tx_gain_1_1);
+ else if (AR_SREV_9580(ah))
+@@ -492,7 +492,7 @@ static void ar9003_tx_gain_table_mode2(s
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9340Modes_low_ob_db_tx_gain_table_1p0);
+- else if (AR_SREV_9485_11(ah))
++ else if (AR_SREV_9485_11_OR_LATER(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9485Modes_low_ob_db_tx_gain_1_1);
+ else if (AR_SREV_9580(ah))
+@@ -517,7 +517,7 @@ static void ar9003_tx_gain_table_mode3(s
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9340Modes_high_power_tx_gain_table_1p0);
+- else if (AR_SREV_9485_11(ah))
++ else if (AR_SREV_9485_11_OR_LATER(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9485Modes_high_power_tx_gain_1_1);
+ else if (AR_SREV_9580(ah))
+@@ -552,7 +552,7 @@ static void ar9003_tx_gain_table_mode4(s
+
+ static void ar9003_tx_gain_table_mode5(struct ath_hw *ah)
+ {
+- if (AR_SREV_9485_11(ah))
++ if (AR_SREV_9485_11_OR_LATER(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9485Modes_green_ob_db_tx_gain_1_1);
+ else if (AR_SREV_9340(ah))
+@@ -571,7 +571,7 @@ static void ar9003_tx_gain_table_mode6(s
+ if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9340Modes_low_ob_db_and_spur_tx_gain_table_1p0);
+- else if (AR_SREV_9485_11(ah))
++ else if (AR_SREV_9485_11_OR_LATER(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ ar9485Modes_green_spur_ob_db_tx_gain_1_1);
+ else if (AR_SREV_9580(ah))
+@@ -611,7 +611,7 @@ static void ar9003_rx_gain_table_mode0(s
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9340Common_rx_gain_table_1p0);
+- else if (AR_SREV_9485_11(ah))
++ else if (AR_SREV_9485_11_OR_LATER(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9485_common_rx_gain_1_1);
+ else if (AR_SREV_9550(ah)) {
+@@ -644,7 +644,7 @@ static void ar9003_rx_gain_table_mode1(s
+ else if (AR_SREV_9340(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9340Common_wo_xlna_rx_gain_table_1p0);
+- else if (AR_SREV_9485_11(ah))
++ else if (AR_SREV_9485_11_OR_LATER(ah))
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ ar9485Common_wo_xlna_rx_gain_1_1);
+ else if (AR_SREV_9462_21(ah))
+@@ -745,16 +745,25 @@ static void ar9003_hw_init_mode_gain_reg
+ static void ar9003_hw_configpcipowersave(struct ath_hw *ah,
+ bool power_off)
+ {
++ /*
++ * Increase L1 Entry Latency. Some WB222 boards don't have
++ * this change in eeprom/OTP.
++ *
++ */
++ if (AR_SREV_9462(ah)) {
++ u32 val = ah->config.aspm_l1_fix;
++ if ((val & 0xff000000) == 0x17000000) {
++ val &= 0x00ffffff;
++ val |= 0x27000000;
++ REG_WRITE(ah, 0x570c, val);
++ }
++ }
++
+ /* Nothing to do on restore for 11N */
+ if (!power_off /* !restore */) {
+ /* set bit 19 to allow forcing of pcie core into L1 state */
+ REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
+-
+- /* Several PCIe massages to ensure proper behaviour */
+- if (ah->config.pcie_waen)
+- REG_WRITE(ah, AR_WA, ah->config.pcie_waen);
+- else
+- REG_WRITE(ah, AR_WA, ah->WARegVal);
++ REG_WRITE(ah, AR_WA, ah->WARegVal);
+ }
+
+ /*
+--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+@@ -491,6 +491,7 @@ int ath9k_hw_process_rxdesc_edma(struct
+ rxs->rs_rate = MS(rxsp->status1, AR_RxRate);
+ rxs->rs_more = (rxsp->status2 & AR_RxMore) ? 1 : 0;
+
++ rxs->rs_firstaggr = (rxsp->status11 & AR_RxFirstAggr) ? 1 : 0;
+ rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0;
+ rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0;
+ rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7);
+--- a/drivers/net/wireless/ath/ath9k/common.c
++++ b/drivers/net/wireless/ath/ath9k/common.c
+@@ -49,37 +49,40 @@ int ath9k_cmn_get_hw_crypto_keytype(stru
+ }
+ EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
+
+-static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan,
+- enum nl80211_channel_type channel_type)
++static u32 ath9k_get_extchanmode(struct cfg80211_chan_def *chandef)
+ {
+ u32 chanmode = 0;
+
+- switch (chan->band) {
++ switch (chandef->chan->band) {
+ case IEEE80211_BAND_2GHZ:
+- switch (channel_type) {
+- case NL80211_CHAN_NO_HT:
+- case NL80211_CHAN_HT20:
++ switch (chandef->width) {
++ case NL80211_CHAN_WIDTH_20_NOHT:
++ case NL80211_CHAN_WIDTH_20:
+ chanmode = CHANNEL_G_HT20;
+ break;
+- case NL80211_CHAN_HT40PLUS:
+- chanmode = CHANNEL_G_HT40PLUS;
++ case NL80211_CHAN_WIDTH_40:
++ if (chandef->center_freq1 > chandef->chan->center_freq)
++ chanmode = CHANNEL_G_HT40PLUS;
++ else
++ chanmode = CHANNEL_G_HT40MINUS;
+ break;
+- case NL80211_CHAN_HT40MINUS:
+- chanmode = CHANNEL_G_HT40MINUS;
++ default:
+ break;
+ }
+ break;
+ case IEEE80211_BAND_5GHZ:
+- switch (channel_type) {
+- case NL80211_CHAN_NO_HT:
+- case NL80211_CHAN_HT20:
++ switch (chandef->width) {
++ case NL80211_CHAN_WIDTH_20_NOHT:
++ case NL80211_CHAN_WIDTH_20:
+ chanmode = CHANNEL_A_HT20;
+ break;
+- case NL80211_CHAN_HT40PLUS:
+- chanmode = CHANNEL_A_HT40PLUS;
++ case NL80211_CHAN_WIDTH_40:
++ if (chandef->center_freq1 > chandef->chan->center_freq)
++ chanmode = CHANNEL_A_HT40PLUS;
++ else
++ chanmode = CHANNEL_A_HT40MINUS;
+ break;
+- case NL80211_CHAN_HT40MINUS:
+- chanmode = CHANNEL_A_HT40MINUS;
++ default:
+ break;
+ }
+ break;
+@@ -94,13 +97,12 @@ static u32 ath9k_get_extchanmode(struct
+ * Update internal channel flags.
+ */
+ void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
+- struct ieee80211_channel *chan,
+- enum nl80211_channel_type channel_type)
++ struct cfg80211_chan_def *chandef)
+ {
+- ichan->channel = chan->center_freq;
+- ichan->chan = chan;
++ ichan->channel = chandef->chan->center_freq;
++ ichan->chan = chandef->chan;
+
+- if (chan->band == IEEE80211_BAND_2GHZ) {
++ if (chandef->chan->band == IEEE80211_BAND_2GHZ) {
+ ichan->chanmode = CHANNEL_G;
+ ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM;
+ } else {
+@@ -108,8 +110,22 @@ void ath9k_cmn_update_ichannel(struct at
+ ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
+ }
+
+- if (channel_type != NL80211_CHAN_NO_HT)
+- ichan->chanmode = ath9k_get_extchanmode(chan, channel_type);
++ switch (chandef->width) {
++ case NL80211_CHAN_WIDTH_5:
++ ichan->channelFlags |= CHANNEL_QUARTER;
++ break;
++ case NL80211_CHAN_WIDTH_10:
++ ichan->channelFlags |= CHANNEL_HALF;
++ break;
++ case NL80211_CHAN_WIDTH_20_NOHT:
++ break;
++ case NL80211_CHAN_WIDTH_20:
++ case NL80211_CHAN_WIDTH_40:
++ ichan->chanmode = ath9k_get_extchanmode(chandef);
++ break;
++ default:
++ WARN_ON(1);
++ }
+ }
+ EXPORT_SYMBOL(ath9k_cmn_update_ichannel);
+
+@@ -125,8 +141,7 @@ struct ath9k_channel *ath9k_cmn_get_curc
+
+ chan_idx = curchan->hw_value;
+ channel = &ah->channels[chan_idx];
+- ath9k_cmn_update_ichannel(channel, curchan,
+- cfg80211_get_chandef_type(&hw->conf.chandef));
++ ath9k_cmn_update_ichannel(channel, &hw->conf.chandef);
+
+ return channel;
+ }
+--- a/drivers/net/wireless/ath/ath9k/common.h
++++ b/drivers/net/wireless/ath/ath9k/common.h
+@@ -44,8 +44,7 @@
+
+ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
+ void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
+- struct ieee80211_channel *chan,
+- enum nl80211_channel_type channel_type);
++ struct cfg80211_chan_def *chandef);
+ struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
+ struct ath_hw *ah);
+ int ath9k_cmn_count_streams(unsigned int chainmask, int max);
+--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
++++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
+@@ -115,10 +115,10 @@ static int hif_usb_send_regout(struct hi
+ cmd->skb = skb;
+ cmd->hif_dev = hif_dev;
+
+- usb_fill_bulk_urb(urb, hif_dev->udev,
+- usb_sndbulkpipe(hif_dev->udev, USB_REG_OUT_PIPE),
++ usb_fill_int_urb(urb, hif_dev->udev,
++ usb_sndintpipe(hif_dev->udev, USB_REG_OUT_PIPE),
+ skb->data, skb->len,
+- hif_usb_regout_cb, cmd);
++ hif_usb_regout_cb, cmd, 1);
+
+ usb_anchor_urb(urb, &hif_dev->regout_submitted);
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+@@ -723,11 +723,11 @@ static void ath9k_hif_usb_reg_in_cb(stru
+ return;
+ }
+
+- usb_fill_bulk_urb(urb, hif_dev->udev,
+- usb_rcvbulkpipe(hif_dev->udev,
++ usb_fill_int_urb(urb, hif_dev->udev,
++ usb_rcvintpipe(hif_dev->udev,
+ USB_REG_IN_PIPE),
+ nskb->data, MAX_REG_IN_BUF_SIZE,
+- ath9k_hif_usb_reg_in_cb, nskb);
++ ath9k_hif_usb_reg_in_cb, nskb, 1);
+ }
+
+ resubmit:
+@@ -909,11 +909,11 @@ static int ath9k_hif_usb_alloc_reg_in_ur
+ goto err_skb;
+ }
+
+- usb_fill_bulk_urb(urb, hif_dev->udev,
+- usb_rcvbulkpipe(hif_dev->udev,
++ usb_fill_int_urb(urb, hif_dev->udev,
++ usb_rcvintpipe(hif_dev->udev,
+ USB_REG_IN_PIPE),
+ skb->data, MAX_REG_IN_BUF_SIZE,
+- ath9k_hif_usb_reg_in_cb, skb);
++ ath9k_hif_usb_reg_in_cb, skb, 1);
+
+ /* Anchor URB */
+ usb_anchor_urb(urb, &hif_dev->reg_in_submitted);
+@@ -1031,9 +1031,7 @@ static int ath9k_hif_usb_download_fw(str
+
+ static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev)
+ {
+- struct usb_host_interface *alt = &hif_dev->interface->altsetting[0];
+- struct usb_endpoint_descriptor *endp;
+- int ret, idx;
++ int ret;
+
+ ret = ath9k_hif_usb_download_fw(hif_dev);
+ if (ret) {
+@@ -1043,20 +1041,6 @@ static int ath9k_hif_usb_dev_init(struct
+ return ret;
+ }
+
+- /* On downloading the firmware to the target, the USB descriptor of EP4
+- * is 'patched' to change the type of the endpoint to Bulk. This will
+- * bring down CPU usage during the scan period.
+- */
+- for (idx = 0; idx < alt->desc.bNumEndpoints; idx++) {
+- endp = &alt->endpoint[idx].desc;
+- if ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+- == USB_ENDPOINT_XFER_INT) {
+- endp->bmAttributes &= ~USB_ENDPOINT_XFERTYPE_MASK;
+- endp->bmAttributes |= USB_ENDPOINT_XFER_BULK;
+- endp->bInterval = 0;
+- }
+- }
+-
+ /* Alloc URBs */
+ ret = ath9k_hif_usb_alloc_urbs(hif_dev);
+ if (ret) {
+@@ -1268,7 +1252,7 @@ static void ath9k_hif_usb_reboot(struct
+ if (!buf)
+ return;
+
+- ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, USB_REG_OUT_PIPE),
++ ret = usb_interrupt_msg(udev, usb_sndintpipe(udev, USB_REG_OUT_PIPE),
+ buf, 4, NULL, HZ);
+ if (ret)
+ dev_err(&udev->dev, "ath9k_htc: USB reboot failed\n");
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+@@ -1203,16 +1203,13 @@ static int ath9k_htc_config(struct ieee8
+
+ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || chip_reset) {
+ struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+- enum nl80211_channel_type channel_type =
+- cfg80211_get_chandef_type(&hw->conf.chandef);
+ int pos = curchan->hw_value;
+
+ ath_dbg(common, CONFIG, "Set channel: %d MHz\n",
+ curchan->center_freq);
+
+ ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
+- hw->conf.chandef.chan,
+- channel_type);
++ &hw->conf.chandef);
+
+ if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
+ ath_err(common, "Unable to set channel\n");
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+@@ -448,6 +448,7 @@ static void ath9k_htc_tx_process(struct
+ struct ieee80211_conf *cur_conf = &priv->hw->conf;
+ bool txok;
+ int slot;
++ int hdrlen, padsize;
+
+ slot = strip_drv_header(priv, skb);
+ if (slot < 0) {
+@@ -504,6 +505,15 @@ send_mac80211:
+
+ ath9k_htc_tx_clear_slot(priv, slot);
+
++ /* Remove padding before handing frame back to mac80211 */
++ hdrlen = ieee80211_get_hdrlen_from_skb(skb);
++
++ padsize = hdrlen & 3;
++ if (padsize && skb->len > hdrlen + padsize) {
++ memmove(skb->data + padsize, skb->data, hdrlen);
++ skb_pull(skb, padsize);
++ }
++
+ /* Send status to mac80211 */
+ ieee80211_tx_status(priv->hw, skb);
+ }
+--- a/drivers/net/wireless/ath/ath9k/link.c
++++ b/drivers/net/wireless/ath/ath9k/link.c
+@@ -41,7 +41,7 @@ void ath_tx_complete_poll_work(struct wo
+ txq->axq_tx_inprogress = true;
+ }
+ }
+- ath_txq_unlock_complete(sc, txq);
++ ath_txq_unlock(sc, txq);
+ }
+
+ if (needreset) {
+--- a/drivers/net/wireless/ath/ath9k/mac.c
++++ b/drivers/net/wireless/ath/ath9k/mac.c
+@@ -583,9 +583,9 @@ int ath9k_hw_rxprocdesc(struct ath_hw *a
+ rs->rs_rate = MS(ads.ds_rxstatus0, AR_RxRate);
+ rs->rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0;
+
++ rs->rs_firstaggr = (ads.ds_rxstatus8 & AR_RxFirstAggr) ? 1 : 0;
+ rs->rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0;
+- rs->rs_moreaggr =
+- (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
++ rs->rs_moreaggr = (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
+ rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna);
+
+ /* directly mapped flags for ieee80211_rx_status */
+--- a/drivers/net/wireless/ath/ath9k/mac.h
++++ b/drivers/net/wireless/ath/ath9k/mac.h
+@@ -140,6 +140,7 @@ struct ath_rx_status {
+ int8_t rs_rssi_ext1;
+ int8_t rs_rssi_ext2;
+ u8 rs_isaggr;
++ u8 rs_firstaggr;
+ u8 rs_moreaggr;
+ u8 rs_num_delims;
+ u8 rs_flags;
+@@ -569,6 +570,7 @@ struct ar5416_desc {
+ #define AR_RxAggr 0x00020000
+ #define AR_PostDelimCRCErr 0x00040000
+ #define AR_RxStatusRsvd71 0x3ff80000
++#define AR_RxFirstAggr 0x20000000
+ #define AR_DecryptBusyErr 0x40000000
+ #define AR_KeyMiss 0x80000000
+
+--- a/drivers/net/wireless/ath/ath9k/rc.c
++++ b/drivers/net/wireless/ath/ath9k/rc.c
+@@ -1324,8 +1324,8 @@ static void ath_rate_update(void *priv,
+ ath_rc_init(sc, priv_sta);
+
+ ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG,
+- "Operating HT Bandwidth changed to: %d\n",
+- cfg80211_get_chandef_type(&sc->hw->conf.chandef));
++ "Operating Bandwidth changed to: %d\n",
++ sc->hw->conf.chandef.width);
+ }
+ }
+
+--- a/drivers/net/wireless/ath/ath9k/reg.h
++++ b/drivers/net/wireless/ath/ath9k/reg.h
+@@ -893,9 +893,9 @@
+
+ #define AR_SREV_9485(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485))
+-#define AR_SREV_9485_11(_ah) \
+- (AR_SREV_9485(_ah) && \
+- ((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_11))
++#define AR_SREV_9485_11_OR_LATER(_ah) \
++ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485) && \
++ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9485_11))
+ #define AR_SREV_9485_OR_LATER(_ah) \
+ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9485))
+