Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[pandora-kernel.git] / drivers / net / wireless / ath / ath9k / main.c
index 422be26..1447b55 100644 (file)
@@ -73,7 +73,7 @@ static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc,
 
        chan_idx = curchan->hw_value;
        channel = &sc->sc_ah->channels[chan_idx];
-       ath9k_update_ichannel(sc, hw, channel);
+       ath9k_cmn_update_ichannel(channel, curchan, hw->conf.channel_type);
        return channel;
 }
 
@@ -230,6 +230,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
        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);
 
@@ -290,6 +291,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
                if (sc->sc_flags & SC_OP_BEACONS)
                        ath_beacon_config(sc, NULL);
                ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+               ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
                ath_start_ani(common);
        }
 
@@ -329,6 +331,8 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int
 {
        struct ieee80211_hw *hw = sc->hw;
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
        struct ath_tx_control txctl;
        int time_left;
 
@@ -346,8 +350,12 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int
        init_completion(&sc->paprd_complete);
        sc->paprd_pending = true;
        txctl.paprd = BIT(chain);
-       if (ath_tx_start(hw, skb, &txctl) != 0)
+
+       if (ath_tx_start(hw, skb, &txctl) != 0) {
+               ath_dbg(common, ATH_DBG_XMIT, "PAPRD TX failed\n");
+               dev_kfree_skb_any(skb);
                return false;
+       }
 
        time_left = wait_for_completion_timeout(&sc->paprd_complete,
                        msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
@@ -609,14 +617,12 @@ void ath9k_tasklet(unsigned long data)
        u32 status = sc->intrstatus;
        u32 rxmask;
 
-       ath9k_ps_wakeup(sc);
-
        if (status & ATH9K_INT_FATAL) {
                ath_reset(sc, true);
-               ath9k_ps_restore(sc);
                return;
        }
 
+       ath9k_ps_wakeup(sc);
        spin_lock(&sc->sc_pcu_lock);
 
        /*
@@ -806,48 +812,6 @@ chip_reset:
 #undef SCHED_INTR
 }
 
-static u32 ath_get_extchanmode(struct ath_softc *sc,
-                              struct ieee80211_channel *chan,
-                              enum nl80211_channel_type channel_type)
-{
-       u32 chanmode = 0;
-
-       switch (chan->band) {
-       case IEEE80211_BAND_2GHZ:
-               switch(channel_type) {
-               case NL80211_CHAN_NO_HT:
-               case NL80211_CHAN_HT20:
-                       chanmode = CHANNEL_G_HT20;
-                       break;
-               case NL80211_CHAN_HT40PLUS:
-                       chanmode = CHANNEL_G_HT40PLUS;
-                       break;
-               case NL80211_CHAN_HT40MINUS:
-                       chanmode = CHANNEL_G_HT40MINUS;
-                       break;
-               }
-               break;
-       case IEEE80211_BAND_5GHZ:
-               switch(channel_type) {
-               case NL80211_CHAN_NO_HT:
-               case NL80211_CHAN_HT20:
-                       chanmode = CHANNEL_A_HT20;
-                       break;
-               case NL80211_CHAN_HT40PLUS:
-                       chanmode = CHANNEL_A_HT40PLUS;
-                       break;
-               case NL80211_CHAN_HT40MINUS:
-                       chanmode = CHANNEL_A_HT40MINUS;
-                       break;
-               }
-               break;
-       default:
-               break;
-       }
-
-       return chanmode;
-}
-
 static void ath9k_bss_assoc_info(struct ath_softc *sc,
                                 struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
@@ -979,8 +943,6 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
 
        spin_unlock_bh(&sc->sc_pcu_lock);
        ath9k_ps_restore(sc);
-
-       ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
 }
 
 int ath_reset(struct ath_softc *sc, bool retry_tx)
@@ -993,6 +955,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
        /* Stop ANI */
        del_timer_sync(&common->ani.timer);
 
+       ath9k_ps_wakeup(sc);
        spin_lock_bh(&sc->sc_pcu_lock);
 
        ieee80211_stop_queues(hw);
@@ -1039,34 +1002,11 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
 
        /* Start ANI */
        ath_start_ani(common);
+       ath9k_ps_restore(sc);
 
        return r;
 }
 
-/* XXX: Remove me once we don't depend on ath9k_channel for all
- * this redundant data */
-void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
-                          struct ath9k_channel *ichan)
-{
-       struct ieee80211_channel *chan = hw->conf.channel;
-       struct ieee80211_conf *conf = &hw->conf;
-
-       ichan->channel = chan->center_freq;
-       ichan->chan = chan;
-
-       if (chan->band == IEEE80211_BAND_2GHZ) {
-               ichan->chanmode = CHANNEL_G;
-               ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G;
-       } else {
-               ichan->chanmode = CHANNEL_A;
-               ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
-       }
-
-       if (conf_is_ht(conf))
-               ichan->chanmode = ath_get_extchanmode(sc, chan,
-                                           conf->channel_type);
-}
-
 /**********************/
 /* mac80211 callbacks */
 /**********************/
@@ -1263,6 +1203,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
                cancel_delayed_work_sync(&sc->ath_led_blink_work);
 
        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);
 
@@ -1283,6 +1224,9 @@ static void ath9k_stop(struct ieee80211_hw *hw)
 
        spin_lock_bh(&sc->sc_pcu_lock);
 
+       /* prevent tasklets to enable interrupts once we disable them */
+       ah->imask &= ~ATH9K_INT_GLOBAL;
+
        /* make sure h/w will not generate any interrupt
         * before setting the invalid flag. */
        ath9k_hw_disable_interrupts(ah);
@@ -1294,12 +1238,23 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        } else
                sc->rx.rxlink = NULL;
 
+       if (sc->rx.frag) {
+               dev_kfree_skb_any(sc->rx.frag);
+               sc->rx.frag = NULL;
+       }
+
        /* disable HAL and put h/w to sleep */
        ath9k_hw_disable(ah);
        ath9k_hw_configpcipowersave(ah, 1, 1);
 
        spin_unlock_bh(&sc->sc_pcu_lock);
 
+       /* we can now sync irq and kill any running tasklets, since we already
+        * disabled interrupts and not holding a spin lock */
+       synchronize_irq(sc->irq);
+       tasklet_kill(&sc->intr_tq);
+       tasklet_kill(&sc->bcon_tasklet);
+
        ath9k_ps_restore(sc);
 
        sc->ps_idle = true;
@@ -1719,8 +1674,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
                        curchan->center_freq);
 
-               /* XXX: remove me eventualy */
-               ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]);
+               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);
@@ -1764,7 +1719,9 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
                sc->config.txpowlimit = 2 * conf->power_level;
+               ath9k_ps_wakeup(sc);
                ath_update_txpow(sc);
+               ath9k_ps_restore(sc);
        }
 
        if (disable_radio) {