]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blobdiff - drivers/net/wireless/ath/ath9k/main.c
Merge branch 'tip/perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
[mirror_ubuntu-eoan-kernel.git] / drivers / net / wireless / ath / ath9k / main.c
index 6530694a59aed691759f3d509284b29a34974292..d2348a5a7809bd1e2233f39a2b6f192443234f2f 100644 (file)
@@ -111,24 +111,29 @@ void ath9k_ps_wakeup(struct ath_softc *sc)
 void ath9k_ps_restore(struct ath_softc *sc)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       enum ath9k_power_mode mode;
        unsigned long flags;
 
        spin_lock_irqsave(&sc->sc_pm_lock, flags);
        if (--sc->ps_usecount != 0)
                goto unlock;
 
-       spin_lock(&common->cc_lock);
-       ath_hw_cycle_counters_update(common);
-       spin_unlock(&common->cc_lock);
-
        if (sc->ps_idle)
-               ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
+               mode = ATH9K_PM_FULL_SLEEP;
        else if (sc->ps_enabled &&
                 !(sc->ps_flags & (PS_WAIT_FOR_BEACON |
                              PS_WAIT_FOR_CAB |
                              PS_WAIT_FOR_PSPOLL_DATA |
                              PS_WAIT_FOR_TX_ACK)))
-               ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
+               mode = ATH9K_PM_NETWORK_SLEEP;
+       else
+               goto unlock;
+
+       spin_lock(&common->cc_lock);
+       ath_hw_cycle_counters_update(common);
+       spin_unlock(&common->cc_lock);
+
+       ath9k_hw_setpower(sc->sc_ah, mode);
 
  unlock:
        spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
@@ -163,7 +168,7 @@ static void ath_update_survey_nf(struct ath_softc *sc, int channel)
 
        if (chan->noisefloor) {
                survey->filled |= SURVEY_INFO_NOISE_DBM;
-               survey->noise = chan->noisefloor;
+               survey->noise = ath9k_hw_getchan_noise(ah, chan);
        }
 }
 
@@ -212,104 +217,168 @@ static int ath_update_survey_stats(struct ath_softc *sc)
        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_debug_samp_bb_mac(sc);
+       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, 1, true);
+               ath_rx_tasklet(sc, 1, 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,
                               sc->config.txpowlimit, &sc->curtxpow);
-       ath9k_hw_set_interrupts(ah, ah->imask);
+       ath9k_hw_set_interrupts(ah);
+       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);
+       if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && 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;
+}
+
+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 r;
+}
+
+
+/*
+ * 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;
 }
 
@@ -317,7 +386,6 @@ static void ath_paprd_activate(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath9k_hw_cal_data *caldata = ah->caldata;
-       struct ath_common *common = ath9k_hw_common(ah);
        int chain;
 
        if (!caldata || !caldata->paprd_done)
@@ -326,7 +394,7 @@ static void ath_paprd_activate(struct ath_softc *sc)
        ath9k_ps_wakeup(sc);
        ar9003_paprd_enable(ah, false);
        for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
-               if (!(common->tx_chainmask & BIT(chain)))
+               if (!(ah->txchainmask & BIT(chain)))
                        continue;
 
                ar9003_paprd_populate_single_table(ah, caldata, chain);
@@ -413,7 +481,7 @@ void ath_paprd_calibrate(struct work_struct *work)
        memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
 
        for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
-               if (!(common->tx_chainmask & BIT(chain)))
+               if (!(ah->txchainmask & BIT(chain)))
                        continue;
 
                chain_ok = 0;
@@ -534,7 +602,7 @@ void ath_ani_calibrate(unsigned long data)
        if (longcal || shortcal) {
                common->ani.caldone =
                        ath9k_hw_calibrate(ah, ah->curchan,
-                                               common->rx_chainmask, longcal);
+                                               ah->rxchainmask, longcal);
        }
 
        ath9k_ps_restore(sc);
@@ -545,6 +613,7 @@ set_timer:
        * The interval must be the shortest necessary to satisfy ANI,
        * short calibration and long calibration.
        */
+       ath9k_debug_samp_bb_mac(sc);
        cal_interval = ATH_LONG_CALINTERVAL;
        if (sc->sc_ah->config.enable_ani)
                cal_interval = min(cal_interval,
@@ -564,7 +633,6 @@ set_timer:
 static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
 {
        struct ath_node *an;
-       struct ath_hw *ah = sc->sc_ah;
        an = (struct ath_node *)sta->drv_priv;
 
 #ifdef CONFIG_ATH9K_DEBUGFS
@@ -573,9 +641,6 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
        spin_unlock(&sc->nodes_lock);
        an->sta = sta;
 #endif
-       if ((ah->caps.hw_caps) & ATH9K_HW_CAP_APM)
-               sc->sc_flags |= SC_OP_ENABLE_APM;
-
        if (sc->sc_flags & SC_OP_TXAGGR) {
                ath_tx_node_init(sc, an);
                an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
@@ -599,74 +664,6 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
                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)
 {
@@ -677,16 +674,24 @@ void ath9k_tasklet(unsigned long data)
        u32 status = sc->intrstatus;
        u32 rxmask;
 
+       ath9k_ps_wakeup(sc);
+       spin_lock(&sc->sc_pcu_lock);
+
        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);
-               return;
-       }
+#ifdef CONFIG_ATH9K_DEBUGFS
+               enum ath_reset_type type;
 
-       ath9k_ps_wakeup(sc);
-       spin_lock(&sc->sc_pcu_lock);
+               if (status & ATH9K_INT_FATAL)
+                       type = RESET_TYPE_FATAL_INT;
+               else
+                       type = RESET_TYPE_BB_WATCHDOG;
+
+               RESET_STAT_INC(sc, type);
+#endif
+               ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+               goto out;
+       }
 
        /*
         * Only run the baseband hang check if beacons stop working in AP or
@@ -706,8 +711,7 @@ void ath9k_tasklet(unsigned long data)
                 */
                ath_dbg(common, ATH_DBG_PS,
                        "TSFOOR - Sync with next Beacon\n");
-               sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC |
-                               PS_TSFOOR_SYNC;
+               sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC;
        }
 
        if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
@@ -736,6 +740,7 @@ void ath9k_tasklet(unsigned long data)
                if (status & ATH9K_INT_GENTIMER)
                        ath_gen_timer_isr(sc->sc_ah);
 
+out:
        /* re-enable hardware interrupt */
        ath9k_hw_enable_interrupts(ah);
 
@@ -826,11 +831,9 @@ irqreturn_t ath_isr(int irq, void *dev)
        if (status & ATH9K_INT_TXURN)
                ath9k_hw_updatetxtriglevel(ah, true);
 
-       if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
-               if (status & ATH9K_INT_RXEOL) {
-                       ah->imask &= ~(ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
-                       ath9k_hw_set_interrupts(ah, ah->imask);
-               }
+       if (status & ATH9K_INT_RXEOL) {
+               ah->imask &= ~(ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
+               ath9k_hw_set_interrupts(ah);
        }
 
        if (status & ATH9K_INT_MIB) {
@@ -886,8 +889,9 @@ static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
 
        ath9k_ps_wakeup(sc);
        spin_lock_bh(&sc->sc_pcu_lock);
+       atomic_set(&ah->intr_ref_cnt, -1);
 
-       ath9k_hw_configpcipowersave(ah, 0, 0);
+       ath9k_hw_configpcipowersave(ah, false);
 
        if (!ah->curchan)
                ah->curchan = ath9k_cmn_get_curchannel(sc->hw, ah);
@@ -899,27 +903,13 @@ static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
                        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);
+       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);
@@ -932,11 +922,10 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
        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
@@ -947,13 +936,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
                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);
@@ -967,55 +950,19 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
 
        ath9k_hw_phy_disable(ah);
 
-       ath9k_hw_configpcipowersave(ah, 1, 1);
+       ath9k_hw_configpcipowersave(ah, true);
 
        spin_unlock_bh(&sc->sc_pcu_lock);
        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);
-       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);
+       r = ath_reset_internal(sc, NULL, retry_tx);
 
        if (retry_tx) {
                int i;
@@ -1028,15 +975,83 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
                }
        }
 
-       ieee80211_wake_queues(hw);
+       ath9k_ps_restore(sc);
 
-       /* Start ANI */
-       if (!common->disable_ani)
-               ath_start_ani(common);
+       return r;
+}
+
+void ath_reset_work(struct work_struct *work)
+{
+       struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work);
 
+       ath_reset(sc, true);
+}
+
+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) {
+                       RESET_STAT_INC(sc, RESET_TYPE_BB_HANG);
+                       ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+               }
+
+       } else if (busy >= 0)
+               sc->hw_busy_count = 0;
+
+out:
        ath9k_ps_restore(sc);
+}
 
-       return r;
+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");
+                       RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG);
+                       ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+                       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);
+       }
 }
 
 /**********************/
@@ -1066,7 +1081,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
        init_channel = ath9k_cmn_get_curchannel(hw, ah);
 
        /* Reset SERDES registers */
-       ath9k_hw_configpcipowersave(ah, 0, 0);
+       ath9k_hw_configpcipowersave(ah, false);
 
        /*
         * The basic interface to setting the hardware in a good
@@ -1085,28 +1100,6 @@ static int ath9k_start(struct ieee80211_hw *hw)
                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 |
@@ -1129,11 +1122,14 @@ static int ath9k_start(struct ieee80211_hw *hw)
 
        /* Disable BMISS interrupt when we're not associated */
        ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
-       ath9k_hw_set_interrupts(ah, ah->imask);
 
-       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) {
@@ -1141,8 +1137,6 @@ static int ath9k_start(struct ieee80211_hw *hw)
                                           AR_STOMP_LOW_WLAN_WGHT);
                ath9k_hw_btcoex_enable(ah);
 
-               if (common->bus_ops->bt_coex_prep)
-                       common->bus_ops->bt_coex_prep(common);
                if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
                        ath9k_btcoex_timer_resume(sc);
        }
@@ -1228,10 +1222,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
 
        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");
@@ -1418,7 +1409,7 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
                ah->imask &= ~ATH9K_INT_TSFOOR;
        }
 
-       ath9k_hw_set_interrupts(ah, ah->imask);
+       ath9k_hw_set_interrupts(ah);
 
        /* Set up ANI */
        if (iter_data.naps > 0) {
@@ -1593,7 +1584,7 @@ static void ath9k_enable_ps(struct ath_softc *sc)
        if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
                if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) {
                        ah->imask |= ATH9K_INT_TIM_TIMER;
-                       ath9k_hw_set_interrupts(ah, ah->imask);
+                       ath9k_hw_set_interrupts(ah);
                }
                ath9k_hw_setrxabort(ah, 1);
        }
@@ -1613,7 +1604,7 @@ static void ath9k_disable_ps(struct ath_softc *sc)
                                  PS_WAIT_FOR_TX_ACK);
                if (ah->imask & ATH9K_INT_TIM_TIMER) {
                        ah->imask &= ~ATH9K_INT_TIM_TIMER;
-                       ath9k_hw_set_interrupts(ah, ah->imask);
+                       ath9k_hw_set_interrupts(ah);
                }
        }
 
@@ -1676,6 +1667,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
                struct ieee80211_channel *curchan = hw->conf.channel;
+               struct ath9k_channel old_chan;
                int pos = curchan->hw_value;
                int old_pos = -1;
                unsigned long flags;
@@ -1692,14 +1684,24 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                        "Set channel: %d MHz type: %d\n",
                        curchan->center_freq, conf->channel_type);
 
-               ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
-                                         curchan, conf->channel_type);
-
                /* update survey stats for the old channel before switching */
                spin_lock_irqsave(&common->cc_lock, flags);
                ath_update_survey_stats(sc);
                spin_unlock_irqrestore(&common->cc_lock, flags);
 
+               /*
+                * Preserve the current channel values, before updating
+                * the same channel
+                */
+               if (old_pos == pos) {
+                       memcpy(&old_chan, &sc->sc_ah->channels[pos],
+                               sizeof(struct ath9k_channel));
+                       ah->curchan = &old_chan;
+               }
+
+               ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
+                                         curchan, conf->channel_type);
+
                /*
                 * If the operating channel changes, change the survey in-use flags
                 * along with it.
@@ -1844,8 +1846,7 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw,
        switch (cmd) {
        case STA_NOTIFY_SLEEP:
                an->sleeping = true;
-               if (ath_tx_aggr_sleep(sc, an))
-                       ieee80211_sta_set_tim(sta);
+               ath_tx_aggr_sleep(sta, sc, an);
                break;
        case STA_NOTIFY_AWAKE:
                an->sleeping = false;
@@ -1854,7 +1855,8 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw,
        }
 }
 
-static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue,
+static int ath9k_conf_tx(struct ieee80211_hw *hw,
+                        struct ieee80211_vif *vif, u16 queue,
                         const struct ieee80211_tx_queue_params *params)
 {
        struct ath_softc *sc = hw->priv;
@@ -2032,6 +2034,7 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif)
                /* Stop ANI */
                sc->sc_flags &= ~SC_OP_ANI_RUN;
                del_timer_sync(&common->ani.timer);
+               memset(&sc->caldata, 0, sizeof(sc->caldata));
        }
 }
 
@@ -2153,7 +2156,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
        ath9k_ps_restore(sc);
 }
 
-static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
+static u64 ath9k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
        struct ath_softc *sc = hw->priv;
        u64 tsf;
@@ -2167,7 +2170,9 @@ static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
        return tsf;
 }
 
-static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+static void ath9k_set_tsf(struct ieee80211_hw *hw,
+                         struct ieee80211_vif *vif,
+                         u64 tsf)
 {
        struct ath_softc *sc = hw->priv;
 
@@ -2178,7 +2183,7 @@ static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
        mutex_unlock(&sc->mutex);
 }
 
-static void ath9k_reset_tsf(struct ieee80211_hw *hw)
+static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
        struct ath_softc *sc = hw->priv;
 
@@ -2303,6 +2308,12 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
        mutex_lock(&sc->mutex);
        cancel_delayed_work_sync(&sc->tx_complete_work);
 
+       if (ah->ah_flags & AH_UNPLUGGED) {
+               ath_dbg(common, ATH_DBG_ANY, "Device has been unplugged!\n");
+               mutex_unlock(&sc->mutex);
+               return;
+       }
+
        if (sc->sc_flags & SC_OP_INVALID) {
                ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
                mutex_unlock(&sc->mutex);
@@ -2335,9 +2346,11 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
        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);
 
@@ -2400,6 +2413,73 @@ skip:
        return sc->beacon.tx_last;
 }
 
+static int ath9k_get_stats(struct ieee80211_hw *hw,
+                          struct ieee80211_low_level_stats *stats)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_mib_stats *mib_stats = &ah->ah_mibStats;
+
+       stats->dot11ACKFailureCount = mib_stats->ackrcv_bad;
+       stats->dot11RTSFailureCount = mib_stats->rts_bad;
+       stats->dot11FCSErrorCount = mib_stats->fcs_bad;
+       stats->dot11RTSSuccessCount = mib_stats->rts_good;
+       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,
@@ -2424,5 +2504,8 @@ struct ieee80211_ops ath9k_ops = {
        .set_coverage_class = ath9k_set_coverage_class,
        .flush              = ath9k_flush,
        .tx_frames_pending  = ath9k_tx_frames_pending,
-       .tx_last_beacon = ath9k_tx_last_beacon,
+       .tx_last_beacon     = ath9k_tx_last_beacon,
+       .get_stats          = ath9k_get_stats,
+       .set_antenna        = ath9k_set_antenna,
+       .get_antenna        = ath9k_get_antenna,
 };