+++ /dev/null
---- a/drivers/net/wireless/ath/ath9k/main.c
-+++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -212,83 +212,47 @@ static int ath_update_survey_stats(struc
- return ret;
- }
-
--/*
-- * Set/change channels. If the channel is really being changed, it's done
-- * by reseting the chip. To accomplish this we must first cleanup any pending
-- * DMA, then restart stuff.
--*/
--static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
-- struct ath9k_channel *hchan)
-+static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
- {
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
-- struct ieee80211_conf *conf = &common->hw->conf;
-- bool fastcc = true, stopped;
-- struct ieee80211_channel *channel = hw->conf.channel;
-- struct ath9k_hw_cal_data *caldata = NULL;
-- int r;
-+ bool ret;
-
-- if (sc->sc_flags & SC_OP_INVALID)
-- return -EIO;
-+ ieee80211_stop_queues(sc->hw);
-
- sc->hw_busy_count = 0;
--
- del_timer_sync(&common->ani.timer);
- cancel_work_sync(&sc->paprd_work);
- cancel_work_sync(&sc->hw_check_work);
- cancel_delayed_work_sync(&sc->tx_complete_work);
- cancel_delayed_work_sync(&sc->hw_pll_work);
-
-- ath9k_ps_wakeup(sc);
--
-- spin_lock_bh(&sc->sc_pcu_lock);
--
-- /*
-- * This is only performed if the channel settings have
-- * actually changed.
-- *
-- * To switch channels clear any pending DMA operations;
-- * wait long enough for the RX fifo to drain, reset the
-- * hardware at the new frequency, and then re-enable
-- * the relevant bits of the h/w.
-- */
- ath9k_hw_disable_interrupts(ah);
-- stopped = ath_drain_all_txq(sc, false);
--
-- if (!ath_stoprecv(sc))
-- stopped = false;
-
-- if (!ath9k_hw_check_alive(ah))
-- stopped = false;
-+ ret = ath_drain_all_txq(sc, retry_tx);
-
-- /* XXX: do not flush receive queue here. We don't want
-- * to flush data frames already in queue because of
-- * changing channel. */
--
-- if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL))
-- fastcc = false;
-+ if (!ath_stoprecv(sc))
-+ ret = false;
-
-- if (!(sc->sc_flags & SC_OP_OFFCHANNEL))
-- caldata = &sc->caldata;
-+ if (!flush) {
-+ if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
-+ ath_rx_tasklet(sc, 0, true);
-+ ath_rx_tasklet(sc, 0, false);
-+ } else {
-+ ath_flushrecv(sc);
-+ }
-
-- ath_dbg(common, ATH_DBG_CONFIG,
-- "(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n",
-- sc->sc_ah->curchan->channel,
-- channel->center_freq, conf_is_ht40(conf),
-- fastcc);
-+ return ret;
-+}
-
-- r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
-- if (r) {
-- ath_err(common,
-- "Unable to reset channel (%u MHz), reset status %d\n",
-- channel->center_freq, r);
-- goto ps_restore;
-- }
-+static bool ath_complete_reset(struct ath_softc *sc, bool start)
-+{
-+ struct ath_hw *ah = sc->sc_ah;
-+ struct ath_common *common = ath9k_hw_common(ah);
-
- if (ath_startrecv(sc) != 0) {
- ath_err(common, "Unable to restart recv logic\n");
-- r = -EIO;
-- goto ps_restore;
-+ return false;
- }
-
- ath9k_cmn_update_txpow(ah, sc->curtxpow,
-@@ -296,21 +260,89 @@ static int ath_set_channel(struct ath_so
- ath9k_hw_set_interrupts(ah, ah->imask);
- ath9k_hw_enable_interrupts(ah);
-
-- if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) {
-+ if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) && start) {
- if (sc->sc_flags & SC_OP_BEACONS)
- ath_set_beacon(sc);
-+
- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
- ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
- if (!common->disable_ani)
- ath_start_ani(common);
- }
-
-- ps_restore:
-- ieee80211_wake_queues(hw);
-+ ieee80211_wake_queues(sc->hw);
-+
-+ return true;
-+}
-+
-+static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
-+ bool retry_tx)
-+{
-+ struct ath_hw *ah = sc->sc_ah;
-+ struct ath_common *common = ath9k_hw_common(ah);
-+ struct ath9k_hw_cal_data *caldata = NULL;
-+ bool fastcc = true;
-+ bool flush = false;
-+ int r;
-+
-+ if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) {
-+ fastcc = false;
-+ caldata = &sc->caldata;
-+ }
-+
-+ if (!hchan) {
-+ fastcc = false;
-+ flush = true;
-+ hchan = ah->curchan;
-+ }
-+
-+ if (fastcc && !ath9k_hw_check_alive(ah))
-+ fastcc = false;
-+
-+ if (!ath_prepare_reset(sc, retry_tx, flush))
-+ fastcc = false;
-+
-+ ath_dbg(common, ATH_DBG_CONFIG,
-+ "Reset to %u MHz, HT40: %d fastcc: %d\n",
-+ hchan->channel, !!(hchan->channelFlags & (CHANNEL_HT40MINUS |
-+ CHANNEL_HT40PLUS)),
-+ fastcc);
-+
-+ r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
-+ if (r) {
-+ ath_err(common,
-+ "Unable to reset channel, reset status %d\n", r);
-+ return r;
-+ }
-+
-+ if (!ath_complete_reset(sc, true))
-+ return -EIO;
-+
-+ return 0;
-+}
-+
-+
-+/*
-+ * Set/change channels. If the channel is really being changed, it's done
-+ * by reseting the chip. To accomplish this we must first cleanup any pending
-+ * DMA, then restart stuff.
-+*/
-+static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
-+ struct ath9k_channel *hchan)
-+{
-+ int r;
-
-+ if (sc->sc_flags & SC_OP_INVALID)
-+ return -EIO;
-+
-+ ath9k_ps_wakeup(sc);
-+
-+ spin_lock_bh(&sc->sc_pcu_lock);
-+ r = ath_reset_internal(sc, hchan, false);
- spin_unlock_bh(&sc->sc_pcu_lock);
-
- ath9k_ps_restore(sc);
-+
- return r;
- }
-
-@@ -893,28 +925,13 @@ static void ath_radio_enable(struct ath_
- channel->center_freq, r);
- }
-
-- ath9k_cmn_update_txpow(ah, sc->curtxpow,
-- sc->config.txpowlimit, &sc->curtxpow);
-- if (ath_startrecv(sc) != 0) {
-- ath_err(common, "Unable to restart recv logic\n");
-- goto out;
-- }
-- if (sc->sc_flags & SC_OP_BEACONS)
-- ath_set_beacon(sc); /* restart beacons */
--
-- /* Re-Enable interrupts */
-- ath9k_hw_set_interrupts(ah, ah->imask);
-- ath9k_hw_enable_interrupts(ah);
-+ ath_complete_reset(sc, true);
-
- /* Enable LED */
- ath9k_hw_cfg_output(ah, ah->led_pin,
- AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
- ath9k_hw_set_gpio(ah, ah->led_pin, 0);
-
-- ieee80211_wake_queues(hw);
-- ieee80211_queue_delayed_work(hw, &sc->hw_pll_work, HZ/2);
--
--out:
- spin_unlock_bh(&sc->sc_pcu_lock);
-
- ath9k_ps_restore(sc);
-@@ -927,12 +944,8 @@ void ath_radio_disable(struct ath_softc
- int r;
-
- ath9k_ps_wakeup(sc);
-- cancel_delayed_work_sync(&sc->hw_pll_work);
--
- spin_lock_bh(&sc->sc_pcu_lock);
-
-- ieee80211_stop_queues(hw);
--
- /*
- * Keep the LED on when the radio is disabled
- * during idle unassociated state.
-@@ -942,13 +955,7 @@ void ath_radio_disable(struct ath_softc
- ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
- }
-
-- /* Disable interrupts */
-- ath9k_hw_disable_interrupts(ah);
--
-- ath_drain_all_txq(sc, false); /* clear pending tx frames */
--
-- ath_stoprecv(sc); /* turn off frame recv */
-- ath_flushrecv(sc); /* flush recv queue */
-+ ath_prepare_reset(sc, false, true);
-
- if (!ah->curchan)
- ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
-@@ -970,48 +977,11 @@ void ath_radio_disable(struct ath_softc
-
- int ath_reset(struct ath_softc *sc, bool retry_tx)
- {
-- struct ath_hw *ah = sc->sc_ah;
-- struct ath_common *common = ath9k_hw_common(ah);
-- struct ieee80211_hw *hw = sc->hw;
- int r;
-
-- sc->hw_busy_count = 0;
--
-- /* Stop ANI */
--
-- del_timer_sync(&common->ani.timer);
--
- ath9k_ps_wakeup(sc);
-
-- ieee80211_stop_queues(hw);
--
-- ath9k_hw_disable_interrupts(ah);
-- ath_drain_all_txq(sc, retry_tx);
--
-- ath_stoprecv(sc);
-- ath_flushrecv(sc);
--
-- r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
-- if (r)
-- ath_err(common,
-- "Unable to reset hardware; reset status %d\n", r);
--
-- if (ath_startrecv(sc) != 0)
-- ath_err(common, "Unable to start recv logic\n");
--
-- /*
-- * We may be doing a reset in response to a request
-- * that changes the channel so update any state that
-- * might change as a result.
-- */
-- ath9k_cmn_update_txpow(ah, sc->curtxpow,
-- sc->config.txpowlimit, &sc->curtxpow);
--
-- if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL)))
-- ath_set_beacon(sc); /* restart beacons */
--
-- ath9k_hw_set_interrupts(ah, ah->imask);
-- ath9k_hw_enable_interrupts(ah);
-+ r = ath_reset_internal(sc, NULL, retry_tx);
-
- if (retry_tx) {
- int i;
-@@ -1024,12 +994,6 @@ int ath_reset(struct ath_softc *sc, bool
- }
- }
-
-- ieee80211_wake_queues(hw);
--
-- /* Start ANI */
-- if (!common->disable_ani)
-- ath_start_ani(common);
--
- ath9k_ps_restore(sc);
-
- return r;
-@@ -1081,28 +1045,6 @@ static int ath9k_start(struct ieee80211_
- goto mutex_unlock;
- }
-
-- /*
-- * This is needed only to setup initial state
-- * but it's best done after a reset.
-- */
-- ath9k_cmn_update_txpow(ah, sc->curtxpow,
-- sc->config.txpowlimit, &sc->curtxpow);
--
-- /*
-- * Setup the hardware after reset:
-- * The receive engine is set going.
-- * Frame transmit is handled entirely
-- * in the frame output path; there's nothing to do
-- * here except setup the interrupt mask.
-- */
-- if (ath_startrecv(sc) != 0) {
-- ath_err(common, "Unable to start recv logic\n");
-- r = -EIO;
-- spin_unlock_bh(&sc->sc_pcu_lock);
-- goto mutex_unlock;
-- }
-- spin_unlock_bh(&sc->sc_pcu_lock);
--
- /* Setup our intr mask. */
- ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
- ATH9K_INT_RXORN | ATH9K_INT_FATAL |
-@@ -1125,12 +1067,14 @@ static int ath9k_start(struct ieee80211_
-
- /* Disable BMISS interrupt when we're not associated */
- ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
-- ath9k_hw_set_interrupts(ah, ah->imask);
-- ath9k_hw_enable_interrupts(ah);
-
-- ieee80211_wake_queues(hw);
-+ if (!ath_complete_reset(sc, false)) {
-+ r = -EIO;
-+ spin_unlock_bh(&sc->sc_pcu_lock);
-+ goto mutex_unlock;
-+ }
-
-- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
-+ spin_unlock_bh(&sc->sc_pcu_lock);
-
- if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
- !ah->btcoex_hw.enabled) {
--- /dev/null
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -429,6 +429,7 @@ void ath9k_set_beaconing_status(struct a
+
+ #define ATH_PAPRD_TIMEOUT 100 /* msecs */
+
++void ath_reset_work(struct work_struct *work);
+ void ath_hw_check(struct work_struct *work);
+ void ath_hw_pll_work(struct work_struct *work);
+ void ath_paprd_calibrate(struct work_struct *work);
+@@ -609,6 +610,7 @@ struct ath_softc {
+ struct mutex mutex;
+ struct work_struct paprd_work;
+ struct work_struct hw_check_work;
++ struct work_struct hw_reset_work;
+ struct completion paprd_complete;
+
+ unsigned int hw_busy_count;
+@@ -655,7 +657,6 @@ struct ath_softc {
+ };
+
+ void ath9k_tasklet(unsigned long data);
+-int ath_reset(struct ath_softc *sc, bool retry_tx);
+ int ath_cabq_update(struct ath_softc *);
+
+ static inline void ath_read_cachesize(struct ath_common *common, int *csz)
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -776,6 +776,7 @@ int ath9k_init_device(u16 devid, struct
+ goto error_world;
+ }
+
++ INIT_WORK(&sc->hw_reset_work, ath_reset_work);
+ INIT_WORK(&sc->hw_check_work, ath_hw_check);
+ INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
+ INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -595,74 +595,6 @@ static void ath_node_detach(struct ath_s
+ ath_tx_node_cleanup(sc, an);
+ }
+
+-void ath_hw_check(struct work_struct *work)
+-{
+- struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
+- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+- unsigned long flags;
+- int busy;
+-
+- ath9k_ps_wakeup(sc);
+- if (ath9k_hw_check_alive(sc->sc_ah))
+- goto out;
+-
+- spin_lock_irqsave(&common->cc_lock, flags);
+- busy = ath_update_survey_stats(sc);
+- spin_unlock_irqrestore(&common->cc_lock, flags);
+-
+- ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, "
+- "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1);
+- if (busy >= 99) {
+- if (++sc->hw_busy_count >= 3) {
+- spin_lock_bh(&sc->sc_pcu_lock);
+- ath_reset(sc, true);
+- spin_unlock_bh(&sc->sc_pcu_lock);
+- }
+- } else if (busy >= 0)
+- sc->hw_busy_count = 0;
+-
+-out:
+- ath9k_ps_restore(sc);
+-}
+-
+-static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
+-{
+- static int count;
+- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+-
+- if (pll_sqsum >= 0x40000) {
+- count++;
+- if (count == 3) {
+- /* Rx is hung for more than 500ms. Reset it */
+- ath_dbg(common, ATH_DBG_RESET,
+- "Possible RX hang, resetting");
+- spin_lock_bh(&sc->sc_pcu_lock);
+- ath_reset(sc, true);
+- spin_unlock_bh(&sc->sc_pcu_lock);
+- count = 0;
+- }
+- } else
+- count = 0;
+-}
+-
+-void ath_hw_pll_work(struct work_struct *work)
+-{
+- struct ath_softc *sc = container_of(work, struct ath_softc,
+- hw_pll_work.work);
+- u32 pll_sqsum;
+-
+- if (AR_SREV_9485(sc->sc_ah)) {
+-
+- ath9k_ps_wakeup(sc);
+- pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
+- ath9k_ps_restore(sc);
+-
+- ath_hw_pll_rx_hang_check(sc, pll_sqsum);
+-
+- ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5);
+- }
+-}
+-
+
+ void ath9k_tasklet(unsigned long data)
+ {
+@@ -675,9 +607,7 @@ void ath9k_tasklet(unsigned long data)
+
+ if ((status & ATH9K_INT_FATAL) ||
+ (status & ATH9K_INT_BB_WATCHDOG)) {
+- spin_lock(&sc->sc_pcu_lock);
+- ath_reset(sc, true);
+- spin_unlock(&sc->sc_pcu_lock);
++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ return;
+ }
+
+@@ -968,7 +898,7 @@ void ath_radio_disable(struct ath_softc
+ ath9k_ps_restore(sc);
+ }
+
+-int ath_reset(struct ath_softc *sc, bool retry_tx)
++static int ath_reset(struct ath_softc *sc, bool retry_tx)
+ {
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+@@ -1035,6 +965,84 @@ int ath_reset(struct ath_softc *sc, bool
+ return r;
+ }
+
++void ath_reset_work(struct work_struct *work)
++{
++ struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
++
++ spin_lock_bh(&sc->sc_pcu_lock);
++ ath_reset(sc, true);
++ spin_unlock_bh(&sc->sc_pcu_lock);
++}
++
++void ath_hw_check(struct work_struct *work)
++{
++ struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
++ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++ unsigned long flags;
++ int busy;
++
++ ath9k_ps_wakeup(sc);
++ if (ath9k_hw_check_alive(sc->sc_ah))
++ goto out;
++
++ spin_lock_irqsave(&common->cc_lock, flags);
++ busy = ath_update_survey_stats(sc);
++ spin_unlock_irqrestore(&common->cc_lock, flags);
++
++ ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, "
++ "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1);
++ if (busy >= 99) {
++ if (++sc->hw_busy_count >= 3) {
++ spin_lock_bh(&sc->sc_pcu_lock);
++ ath_reset(sc, true);
++ spin_unlock_bh(&sc->sc_pcu_lock);
++ }
++
++ } else if (busy >= 0)
++ sc->hw_busy_count = 0;
++
++out:
++ ath9k_ps_restore(sc);
++}
++
++static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
++{
++ static int count;
++ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++
++ if (pll_sqsum >= 0x40000) {
++ count++;
++ if (count == 3) {
++ /* Rx is hung for more than 500ms. Reset it */
++ ath_dbg(common, ATH_DBG_RESET,
++ "Possible RX hang, resetting");
++ spin_lock_bh(&sc->sc_pcu_lock);
++ ath_reset(sc, true);
++ spin_unlock_bh(&sc->sc_pcu_lock);
++ count = 0;
++ }
++ } else
++ count = 0;
++}
++
++void ath_hw_pll_work(struct work_struct *work)
++{
++ struct ath_softc *sc = container_of(work, struct ath_softc,
++ hw_pll_work.work);
++ u32 pll_sqsum;
++
++ if (AR_SREV_9485(sc->sc_ah)) {
++
++ ath9k_ps_wakeup(sc);
++ pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
++ ath9k_ps_restore(sc);
++
++ ath_hw_pll_rx_hang_check(sc, pll_sqsum);
++
++ ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5);
++ }
++}
++
+ /**********************/
+ /* mac80211 callbacks */
+ /**********************/
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -601,7 +601,7 @@ static void ath_tx_complete_aggr(struct
+ rcu_read_unlock();
+
+ if (needreset)
+- ath_reset(sc, false);
++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ }
+
+ static bool ath_lookup_legacy(struct ath_buf *bf)
+@@ -2268,9 +2268,7 @@ static void ath_tx_complete_poll_work(st
+ if (needreset) {
+ ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET,
+ "tx hung, resetting the chip\n");
+- spin_lock_bh(&sc->sc_pcu_lock);
+- ath_reset(sc, true);
+- spin_unlock_bh(&sc->sc_pcu_lock);
++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ }
+
+ ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
+--- a/drivers/net/wireless/ath/ath9k/beacon.c
++++ b/drivers/net/wireless/ath/ath9k/beacon.c
+@@ -386,9 +386,7 @@ void ath_beacon_tasklet(unsigned long da
+ ath_dbg(common, ATH_DBG_BSTUCK,
+ "beacon is officially stuck\n");
+ sc->sc_flags |= SC_OP_TSF_RESET;
+- spin_lock(&sc->sc_pcu_lock);
+- ath_reset(sc, true);
+- spin_unlock(&sc->sc_pcu_lock);
++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ }
+
+ return;
+++ /dev/null
---- a/drivers/net/wireless/ath/ath9k/init.c
-+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -652,9 +652,22 @@ static void ath9k_init_txpower_limits(st
- ah->curchan = curchan;
- }
-
-+void ath9k_reload_chainmask_settings(struct ath_softc *sc)
-+{
-+ if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT))
-+ return;
-+
-+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
-+ setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
-+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
-+ setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
-+}
-+
-+
- void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
- {
-- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-+ struct ath_hw *ah = sc->sc_ah;
-+ struct ath_common *common = ath9k_hw_common(ah);
-
- hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
- IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-@@ -692,6 +705,16 @@ void ath9k_set_hw_capab(struct ath_softc
- hw->sta_data_size = sizeof(struct ath_node);
- hw->vif_data_size = sizeof(struct ath_vif);
-
-+ hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1;
-+ hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1;
-+
-+ /* single chain devices with rx diversity */
-+ if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
-+ hw->wiphy->available_antennas_rx = BIT(0) | BIT(1);
-+
-+ sc->ant_rx = hw->wiphy->available_antennas_rx;
-+ sc->ant_tx = hw->wiphy->available_antennas_tx;
-+
- #ifdef CONFIG_ATH9K_RATE_CONTROL
- hw->rate_control_algorithm = "ath9k_rate_control";
- #endif
-@@ -703,12 +726,7 @@ void ath9k_set_hw_capab(struct ath_softc
- hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
- &sc->sbands[IEEE80211_BAND_5GHZ];
-
-- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
-- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
-- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
-- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
-- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
-- }
-+ ath9k_reload_chainmask_settings(sc);
-
- SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
- }
---- a/drivers/net/wireless/ath/ath9k/ath9k.h
-+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
-@@ -652,6 +652,7 @@ struct ath_softc {
- struct ath_descdma txsdma;
-
- struct ath_ant_comb ant_comb;
-+ u8 ant_tx, ant_rx;
- };
-
- void ath9k_tasklet(unsigned long data);
-@@ -673,6 +674,7 @@ int ath9k_init_device(u16 devid, struct
- const struct ath_bus_ops *bus_ops);
- void ath9k_deinit_device(struct ath_softc *sc);
- void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
-+void ath9k_reload_chainmask_settings(struct ath_softc *sc);
-
- void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw);
- bool ath9k_uses_beacons(int type);
---- a/drivers/net/wireless/ath/ath9k/main.c
-+++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -270,6 +270,22 @@ static bool ath_complete_reset(struct at
- ath_start_ani(common);
- }
-
-+ if (ath9k_hw_ops(ah)->antdiv_comb_conf_get && sc->ant_rx != 3) {
-+ 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);
-+ }
-+
- ieee80211_wake_queues(sc->hw);
-
- return true;
-@@ -2366,6 +2382,59 @@ static int ath9k_get_stats(struct ieee80
- return 0;
- }
-
-+static u32 fill_chainmask(u32 cap, u32 new)
-+{
-+ u32 filled = 0;
-+ int i;
-+
-+ for (i = 0; cap && new; i++, cap >>= 1) {
-+ if (!(cap & BIT(0)))
-+ continue;
-+
-+ if (new & BIT(0))
-+ filled |= BIT(i);
-+
-+ new >>= 1;
-+ }
-+
-+ return filled;
-+}
-+
-+static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
-+{
-+ struct ath_softc *sc = hw->priv;
-+ struct ath_hw *ah = sc->sc_ah;
-+
-+ if (!rx_ant || !tx_ant)
-+ return -EINVAL;
-+
-+ sc->ant_rx = rx_ant;
-+ sc->ant_tx = tx_ant;
-+
-+ if (ah->caps.rx_chainmask == 1)
-+ return 0;
-+
-+ /* AR9100 runs into calibration issues if not all rx chains are enabled */
-+ if (AR_SREV_9100(ah))
-+ ah->rxchainmask = 0x7;
-+ else
-+ ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant);
-+
-+ ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant);
-+ ath9k_reload_chainmask_settings(sc);
-+
-+ return 0;
-+}
-+
-+static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
-+{
-+ struct ath_softc *sc = hw->priv;
-+
-+ *tx_ant = sc->ant_tx;
-+ *rx_ant = sc->ant_rx;
-+ return 0;
-+}
-+
- struct ieee80211_ops ath9k_ops = {
- .tx = ath9k_tx,
- .start = ath9k_start,
-@@ -2392,4 +2461,6 @@ struct ieee80211_ops ath9k_ops = {
- .tx_frames_pending = ath9k_tx_frames_pending,
- .tx_last_beacon = ath9k_tx_last_beacon,
- .get_stats = ath9k_get_stats,
-+ .set_antenna = ath9k_set_antenna,
-+ .get_antenna = ath9k_get_antenna,
- };
---- a/drivers/net/wireless/ath/ath9k/recv.c
-+++ b/drivers/net/wireless/ath/ath9k/recv.c
-@@ -1956,7 +1956,7 @@ int ath_rx_tasklet(struct ath_softc *sc,
- ath_rx_ps(sc, skb);
- spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
-
-- if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
-+ if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3)
- ath_ant_comb_scan(sc, &rs);
-
- ieee80211_rx(hw, skb);
--- /dev/null
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -212,83 +212,57 @@ static int ath_update_survey_stats(struc
+ return ret;
+ }
+
+-/*
+- * Set/change channels. If the channel is really being changed, it's done
+- * by reseting the chip. To accomplish this we must first cleanup any pending
+- * DMA, then restart stuff.
+-*/
+-static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
+- struct ath9k_channel *hchan)
++static void __ath_cancel_work(struct ath_softc *sc)
+ {
+- struct ath_hw *ah = sc->sc_ah;
+- struct ath_common *common = ath9k_hw_common(ah);
+- struct ieee80211_conf *conf = &common->hw->conf;
+- bool fastcc = true, stopped;
+- struct ieee80211_channel *channel = hw->conf.channel;
+- struct ath9k_hw_cal_data *caldata = NULL;
+- int r;
+-
+- if (sc->sc_flags & SC_OP_INVALID)
+- return -EIO;
+-
+- sc->hw_busy_count = 0;
+-
+- del_timer_sync(&common->ani.timer);
+ cancel_work_sync(&sc->paprd_work);
+ cancel_work_sync(&sc->hw_check_work);
+ cancel_delayed_work_sync(&sc->tx_complete_work);
+ cancel_delayed_work_sync(&sc->hw_pll_work);
++}
+
+- ath9k_ps_wakeup(sc);
++static void ath_cancel_work(struct ath_softc *sc)
++{
++ __ath_cancel_work(sc);
++ cancel_work_sync(&sc->hw_reset_work);
++}
+
+- spin_lock_bh(&sc->sc_pcu_lock);
++static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
++{
++ struct ath_hw *ah = sc->sc_ah;
++ struct ath_common *common = ath9k_hw_common(ah);
++ bool ret;
+
+- /*
+- * This is only performed if the channel settings have
+- * actually changed.
+- *
+- * To switch channels clear any pending DMA operations;
+- * wait long enough for the RX fifo to drain, reset the
+- * hardware at the new frequency, and then re-enable
+- * the relevant bits of the h/w.
+- */
+- ath9k_hw_disable_interrupts(ah);
+- stopped = ath_drain_all_txq(sc, false);
++ ieee80211_stop_queues(sc->hw);
+
+- if (!ath_stoprecv(sc))
+- stopped = false;
++ sc->hw_busy_count = 0;
++ del_timer_sync(&common->ani.timer);
+
+- if (!ath9k_hw_check_alive(ah))
+- stopped = false;
++ ath9k_hw_disable_interrupts(ah);
+
+- /* XXX: do not flush receive queue here. We don't want
+- * to flush data frames already in queue because of
+- * changing channel. */
++ ret = ath_drain_all_txq(sc, retry_tx);
+
+- if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL))
+- fastcc = false;
++ if (!ath_stoprecv(sc))
++ ret = false;
+
+- if (!(sc->sc_flags & SC_OP_OFFCHANNEL))
+- caldata = &sc->caldata;
++ if (!flush) {
++ if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
++ ath_rx_tasklet(sc, 0, true);
++ ath_rx_tasklet(sc, 0, false);
++ } else {
++ ath_flushrecv(sc);
++ }
+
+- ath_dbg(common, ATH_DBG_CONFIG,
+- "(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n",
+- sc->sc_ah->curchan->channel,
+- channel->center_freq, conf_is_ht40(conf),
+- fastcc);
++ return ret;
++}
+
+- r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
+- if (r) {
+- ath_err(common,
+- "Unable to reset channel (%u MHz), reset status %d\n",
+- channel->center_freq, r);
+- goto ps_restore;
+- }
++static bool ath_complete_reset(struct ath_softc *sc, bool start)
++{
++ struct ath_hw *ah = sc->sc_ah;
++ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (ath_startrecv(sc) != 0) {
+ ath_err(common, "Unable to restart recv logic\n");
+- r = -EIO;
+- goto ps_restore;
++ return false;
+ }
+
+ ath9k_cmn_update_txpow(ah, sc->curtxpow,
+@@ -296,21 +270,93 @@ static int ath_set_channel(struct ath_so
+ ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_enable_interrupts(ah);
+
+- if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) {
++ if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) && start) {
+ if (sc->sc_flags & SC_OP_BEACONS)
+ ath_set_beacon(sc);
++
+ ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+ ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
+ if (!common->disable_ani)
+ ath_start_ani(common);
+ }
+
+- ps_restore:
+- ieee80211_wake_queues(hw);
++ ieee80211_wake_queues(sc->hw);
++
++ return true;
++}
++
++static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
++ bool retry_tx)
++{
++ struct ath_hw *ah = sc->sc_ah;
++ struct ath_common *common = ath9k_hw_common(ah);
++ struct ath9k_hw_cal_data *caldata = NULL;
++ bool fastcc = true;
++ bool flush = false;
++ int r;
++
++ __ath_cancel_work(sc);
++
++ spin_lock_bh(&sc->sc_pcu_lock);
+
++ if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) {
++ fastcc = false;
++ caldata = &sc->caldata;
++ }
++
++ if (!hchan) {
++ fastcc = false;
++ flush = true;
++ hchan = ah->curchan;
++ }
++
++ if (fastcc && !ath9k_hw_check_alive(ah))
++ fastcc = false;
++
++ if (!ath_prepare_reset(sc, retry_tx, flush))
++ fastcc = false;
++
++ ath_dbg(common, ATH_DBG_CONFIG,
++ "Reset to %u MHz, HT40: %d fastcc: %d\n",
++ hchan->channel, !!(hchan->channelFlags & (CHANNEL_HT40MINUS |
++ CHANNEL_HT40PLUS)),
++ fastcc);
++
++ r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
++ if (r) {
++ ath_err(common,
++ "Unable to reset channel, reset status %d\n", r);
++ goto out;
++ }
++
++ if (!ath_complete_reset(sc, true))
++ r = -EIO;
++
++out:
+ spin_unlock_bh(&sc->sc_pcu_lock);
++ return 0;
++}
++
++
++/*
++ * Set/change channels. If the channel is really being changed, it's done
++ * by reseting the chip. To accomplish this we must first cleanup any pending
++ * DMA, then restart stuff.
++*/
++static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
++ struct ath9k_channel *hchan)
++{
++ int r;
++
++ if (sc->sc_flags & SC_OP_INVALID)
++ return -EIO;
++
++ ath9k_ps_wakeup(sc);
++
++ r = ath_reset_internal(sc, hchan, false);
+
+ ath9k_ps_restore(sc);
++
+ return r;
+ }
+
+@@ -823,28 +869,13 @@ static void ath_radio_enable(struct ath_
+ channel->center_freq, r);
+ }
+
+- ath9k_cmn_update_txpow(ah, sc->curtxpow,
+- sc->config.txpowlimit, &sc->curtxpow);
+- if (ath_startrecv(sc) != 0) {
+- ath_err(common, "Unable to restart recv logic\n");
+- goto out;
+- }
+- if (sc->sc_flags & SC_OP_BEACONS)
+- ath_set_beacon(sc); /* restart beacons */
+-
+- /* Re-Enable interrupts */
+- ath9k_hw_set_interrupts(ah, ah->imask);
+- ath9k_hw_enable_interrupts(ah);
++ ath_complete_reset(sc, true);
+
+ /* Enable LED */
+ ath9k_hw_cfg_output(ah, ah->led_pin,
+ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+ ath9k_hw_set_gpio(ah, ah->led_pin, 0);
+
+- ieee80211_wake_queues(hw);
+- ieee80211_queue_delayed_work(hw, &sc->hw_pll_work, HZ/2);
+-
+-out:
+ spin_unlock_bh(&sc->sc_pcu_lock);
+
+ ath9k_ps_restore(sc);
+@@ -857,11 +888,10 @@ void ath_radio_disable(struct ath_softc
+ int r;
+
+ ath9k_ps_wakeup(sc);
+- cancel_delayed_work_sync(&sc->hw_pll_work);
+
+- spin_lock_bh(&sc->sc_pcu_lock);
++ ath_cancel_work(sc);
+
+- ieee80211_stop_queues(hw);
++ spin_lock_bh(&sc->sc_pcu_lock);
+
+ /*
+ * Keep the LED on when the radio is disabled
+@@ -872,13 +902,7 @@ void ath_radio_disable(struct ath_softc
+ ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
+ }
+
+- /* Disable interrupts */
+- ath9k_hw_disable_interrupts(ah);
+-
+- ath_drain_all_txq(sc, false); /* clear pending tx frames */
+-
+- ath_stoprecv(sc); /* turn off frame recv */
+- ath_flushrecv(sc); /* flush recv queue */
++ ath_prepare_reset(sc, false, true);
+
+ if (!ah->curchan)
+ ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
+@@ -900,48 +924,11 @@ void ath_radio_disable(struct ath_softc
+
+ static int ath_reset(struct ath_softc *sc, bool retry_tx)
+ {
+- struct ath_hw *ah = sc->sc_ah;
+- struct ath_common *common = ath9k_hw_common(ah);
+- struct ieee80211_hw *hw = sc->hw;
+ int r;
+
+- sc->hw_busy_count = 0;
+-
+- /* Stop ANI */
+-
+- del_timer_sync(&common->ani.timer);
+-
+ ath9k_ps_wakeup(sc);
+
+- ieee80211_stop_queues(hw);
+-
+- ath9k_hw_disable_interrupts(ah);
+- ath_drain_all_txq(sc, retry_tx);
+-
+- ath_stoprecv(sc);
+- ath_flushrecv(sc);
+-
+- r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
+- if (r)
+- ath_err(common,
+- "Unable to reset hardware; reset status %d\n", r);
+-
+- if (ath_startrecv(sc) != 0)
+- ath_err(common, "Unable to start recv logic\n");
+-
+- /*
+- * We may be doing a reset in response to a request
+- * that changes the channel so update any state that
+- * might change as a result.
+- */
+- ath9k_cmn_update_txpow(ah, sc->curtxpow,
+- sc->config.txpowlimit, &sc->curtxpow);
+-
+- if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL)))
+- ath_set_beacon(sc); /* restart beacons */
+-
+- ath9k_hw_set_interrupts(ah, ah->imask);
+- ath9k_hw_enable_interrupts(ah);
++ r = ath_reset_internal(sc, NULL, retry_tx);
+
+ if (retry_tx) {
+ int i;
+@@ -954,12 +941,6 @@ static int ath_reset(struct ath_softc *s
+ }
+ }
+
+- ieee80211_wake_queues(hw);
+-
+- /* Start ANI */
+- if (!common->disable_ani)
+- ath_start_ani(common);
+-
+ ath9k_ps_restore(sc);
+
+ return r;
+@@ -969,9 +950,7 @@ void ath_reset_work(struct work_struct *
+ {
+ struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
+
+- spin_lock_bh(&sc->sc_pcu_lock);
+ ath_reset(sc, true);
+- spin_unlock_bh(&sc->sc_pcu_lock);
+ }
+
+ void ath_hw_check(struct work_struct *work)
+@@ -992,11 +971,8 @@ void ath_hw_check(struct work_struct *wo
+ ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, "
+ "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1);
+ if (busy >= 99) {
+- if (++sc->hw_busy_count >= 3) {
+- spin_lock_bh(&sc->sc_pcu_lock);
+- ath_reset(sc, true);
+- spin_unlock_bh(&sc->sc_pcu_lock);
+- }
++ if (++sc->hw_busy_count >= 3)
++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+
+ } else if (busy >= 0)
+ sc->hw_busy_count = 0;
+@@ -1016,9 +992,7 @@ static void ath_hw_pll_rx_hang_check(str
+ /* Rx is hung for more than 500ms. Reset it */
+ ath_dbg(common, ATH_DBG_RESET,
+ "Possible RX hang, resetting");
+- spin_lock_bh(&sc->sc_pcu_lock);
+- ath_reset(sc, true);
+- spin_unlock_bh(&sc->sc_pcu_lock);
++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ count = 0;
+ }
+ } else
+@@ -1089,28 +1063,6 @@ static int ath9k_start(struct ieee80211_
+ goto mutex_unlock;
+ }
+
+- /*
+- * This is needed only to setup initial state
+- * but it's best done after a reset.
+- */
+- ath9k_cmn_update_txpow(ah, sc->curtxpow,
+- sc->config.txpowlimit, &sc->curtxpow);
+-
+- /*
+- * Setup the hardware after reset:
+- * The receive engine is set going.
+- * Frame transmit is handled entirely
+- * in the frame output path; there's nothing to do
+- * here except setup the interrupt mask.
+- */
+- if (ath_startrecv(sc) != 0) {
+- ath_err(common, "Unable to start recv logic\n");
+- r = -EIO;
+- spin_unlock_bh(&sc->sc_pcu_lock);
+- goto mutex_unlock;
+- }
+- spin_unlock_bh(&sc->sc_pcu_lock);
+-
+ /* Setup our intr mask. */
+ ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
+ ATH9K_INT_RXORN | ATH9K_INT_FATAL |
+@@ -1133,12 +1085,14 @@ static int ath9k_start(struct ieee80211_
+
+ /* Disable BMISS interrupt when we're not associated */
+ ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
+- ath9k_hw_set_interrupts(ah, ah->imask);
+- ath9k_hw_enable_interrupts(ah);
+
+- ieee80211_wake_queues(hw);
++ if (!ath_complete_reset(sc, false)) {
++ r = -EIO;
++ spin_unlock_bh(&sc->sc_pcu_lock);
++ goto mutex_unlock;
++ }
+
+- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
++ spin_unlock_bh(&sc->sc_pcu_lock);
+
+ if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
+ !ah->btcoex_hw.enabled) {
+@@ -1231,10 +1185,7 @@ static void ath9k_stop(struct ieee80211_
+
+ mutex_lock(&sc->mutex);
+
+- cancel_delayed_work_sync(&sc->tx_complete_work);
+- cancel_delayed_work_sync(&sc->hw_pll_work);
+- cancel_work_sync(&sc->paprd_work);
+- cancel_work_sync(&sc->hw_check_work);
++ ath_cancel_work(sc);
+
+ if (sc->sc_flags & SC_OP_INVALID) {
+ ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
+@@ -2351,9 +2302,11 @@ static void ath9k_flush(struct ieee80211
+ ath9k_ps_wakeup(sc);
+ spin_lock_bh(&sc->sc_pcu_lock);
+ drain_txq = ath_drain_all_txq(sc, false);
++ spin_unlock_bh(&sc->sc_pcu_lock);
++
+ if (!drain_txq)
+ ath_reset(sc, false);
+- spin_unlock_bh(&sc->sc_pcu_lock);
++
+ ath9k_ps_restore(sc);
+ ieee80211_wake_queues(hw);
+
--- /dev/null
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -652,9 +652,22 @@ static void ath9k_init_txpower_limits(st
+ ah->curchan = curchan;
+ }
+
++void ath9k_reload_chainmask_settings(struct ath_softc *sc)
++{
++ if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT))
++ return;
++
++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
++ setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
++ setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
++}
++
++
+ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+ {
+- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++ struct ath_hw *ah = sc->sc_ah;
++ struct ath_common *common = ath9k_hw_common(ah);
+
+ hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+ IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+@@ -692,6 +705,16 @@ void ath9k_set_hw_capab(struct ath_softc
+ hw->sta_data_size = sizeof(struct ath_node);
+ hw->vif_data_size = sizeof(struct ath_vif);
+
++ hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1;
++ hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1;
++
++ /* single chain devices with rx diversity */
++ if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
++ hw->wiphy->available_antennas_rx = BIT(0) | BIT(1);
++
++ sc->ant_rx = hw->wiphy->available_antennas_rx;
++ sc->ant_tx = hw->wiphy->available_antennas_tx;
++
+ #ifdef CONFIG_ATH9K_RATE_CONTROL
+ hw->rate_control_algorithm = "ath9k_rate_control";
+ #endif
+@@ -703,12 +726,7 @@ void ath9k_set_hw_capab(struct ath_softc
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &sc->sbands[IEEE80211_BAND_5GHZ];
+
+- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
+- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
+- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
+- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
+- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
+- }
++ ath9k_reload_chainmask_settings(sc);
+
+ SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
+ }
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -654,6 +654,7 @@ struct ath_softc {
+ struct ath_descdma txsdma;
+
+ struct ath_ant_comb ant_comb;
++ u8 ant_tx, ant_rx;
+ };
+
+ void ath9k_tasklet(unsigned long data);
+@@ -674,6 +675,7 @@ int ath9k_init_device(u16 devid, struct
+ const struct ath_bus_ops *bus_ops);
+ void ath9k_deinit_device(struct ath_softc *sc);
+ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
++void ath9k_reload_chainmask_settings(struct ath_softc *sc);
+
+ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw);
+ bool ath9k_uses_beacons(int type);
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -280,6 +280,22 @@ static bool ath_complete_reset(struct at
+ ath_start_ani(common);
+ }
+
++ if (ath9k_hw_ops(ah)->antdiv_comb_conf_get && sc->ant_rx != 3) {
++ 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);
++ }
++
+ ieee80211_wake_queues(sc->hw);
+
+ return true;
+@@ -2383,6 +2399,59 @@ static int ath9k_get_stats(struct ieee80
+ return 0;
+ }
+
++static u32 fill_chainmask(u32 cap, u32 new)
++{
++ u32 filled = 0;
++ int i;
++
++ for (i = 0; cap && new; i++, cap >>= 1) {
++ if (!(cap & BIT(0)))
++ continue;
++
++ if (new & BIT(0))
++ filled |= BIT(i);
++
++ new >>= 1;
++ }
++
++ return filled;
++}
++
++static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
++{
++ struct ath_softc *sc = hw->priv;
++ struct ath_hw *ah = sc->sc_ah;
++
++ if (!rx_ant || !tx_ant)
++ return -EINVAL;
++
++ sc->ant_rx = rx_ant;
++ sc->ant_tx = tx_ant;
++
++ if (ah->caps.rx_chainmask == 1)
++ return 0;
++
++ /* AR9100 runs into calibration issues if not all rx chains are enabled */
++ if (AR_SREV_9100(ah))
++ ah->rxchainmask = 0x7;
++ else
++ ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant);
++
++ ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant);
++ ath9k_reload_chainmask_settings(sc);
++
++ return 0;
++}
++
++static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
++{
++ struct ath_softc *sc = hw->priv;
++
++ *tx_ant = sc->ant_tx;
++ *rx_ant = sc->ant_rx;
++ return 0;
++}
++
+ struct ieee80211_ops ath9k_ops = {
+ .tx = ath9k_tx,
+ .start = ath9k_start,
+@@ -2409,4 +2478,6 @@ struct ieee80211_ops ath9k_ops = {
+ .tx_frames_pending = ath9k_tx_frames_pending,
+ .tx_last_beacon = ath9k_tx_last_beacon,
+ .get_stats = ath9k_get_stats,
++ .set_antenna = ath9k_set_antenna,
++ .get_antenna = ath9k_get_antenna,
+ };
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -1956,7 +1956,7 @@ int ath_rx_tasklet(struct ath_softc *sc,
+ ath_rx_ps(sc, skb);
+ spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+
+- if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
++ if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3)
+ ath_ant_comb_scan(sc, &rs);
+
+ ieee80211_rx(hw, skb);