ath9k: rework power state handling
authorFelix Fietkau <nbd@openwrt.org>
Wed, 16 Nov 2011 12:08:41 +0000 (13:08 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 7 Dec 2011 20:19:34 +0000 (15:19 -0500)
Turning off the radio when mac80211 tells the driver that it's idle is not
a good idea, as idle interfaces might still occasionally scan or send packets.
The only time the radio can be safely turned off is when drv_stop has been
called. In the mean time, use sc->ps_idle only to indicate network sleep vs
full sleep.
Move the LED GPIO changes out of the PCI suspend/resume path, the start/stop
functions already take care of that.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/pci.c

index 3733828e72bc7267e5a09d0a690636b210c5a3db..8b0feecf3faed8ee308b6ee30393ba41ab9a5ca1 100644 (file)
@@ -887,82 +887,6 @@ chip_reset:
 #undef SCHED_INTR
 }
 
-static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
-{
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_channel *channel = hw->conf.channel;
-       int r;
-
-       ath9k_ps_wakeup(sc);
-       spin_lock_bh(&sc->sc_pcu_lock);
-       atomic_set(&ah->intr_ref_cnt, -1);
-
-       ath9k_hw_configpcipowersave(ah, false);
-
-       if (!ah->curchan)
-               ah->curchan = ath9k_cmn_get_curchannel(sc->hw, ah);
-
-       r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
-       if (r) {
-               ath_err(common,
-                       "Unable to reset channel (%u MHz), reset status %d\n",
-                       channel->center_freq, r);
-       }
-
-       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);
-
-       spin_unlock_bh(&sc->sc_pcu_lock);
-
-       ath9k_ps_restore(sc);
-}
-
-void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
-{
-       struct ath_hw *ah = sc->sc_ah;
-       struct ieee80211_channel *channel = hw->conf.channel;
-       int r;
-
-       ath9k_ps_wakeup(sc);
-
-       ath_cancel_work(sc);
-
-       spin_lock_bh(&sc->sc_pcu_lock);
-
-       /*
-        * Keep the LED on when the radio is disabled
-        * during idle unassociated state.
-        */
-       if (!sc->ps_idle) {
-               ath9k_hw_set_gpio(ah, ah->led_pin, 1);
-               ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
-       }
-
-       ath_prepare_reset(sc, false, true);
-
-       if (!ah->curchan)
-               ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
-
-       r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
-       if (r) {
-               ath_err(ath9k_hw_common(sc->sc_ah),
-                       "Unable to reset channel (%u MHz), reset status %d\n",
-                       channel->center_freq, r);
-       }
-
-       ath9k_hw_phy_disable(ah);
-
-       ath9k_hw_configpcipowersave(ah, true);
-
-       spin_unlock_bh(&sc->sc_pcu_lock);
-       ath9k_ps_restore(sc);
-}
-
 static int ath_reset(struct ath_softc *sc, bool retry_tx)
 {
        int r;
@@ -1098,6 +1022,9 @@ static int ath9k_start(struct ieee80211_hw *hw)
         * and then setup of the interrupt mask.
         */
        spin_lock_bh(&sc->sc_pcu_lock);
+
+       atomic_set(&ah->intr_ref_cnt, -1);
+
        r = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
        if (r) {
                ath_err(common,
@@ -1139,6 +1066,18 @@ static int ath9k_start(struct ieee80211_hw *hw)
                goto mutex_unlock;
        }
 
+       if (ah->led_pin >= 0) {
+               ath9k_hw_cfg_output(ah, ah->led_pin,
+                                   AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+               ath9k_hw_set_gpio(ah, ah->led_pin, 0);
+       }
+
+       /*
+        * Reset key cache to sane defaults (all entries cleared) instead of
+        * semi-random values after suspend/resume.
+        */
+       ath9k_cmn_init_crypto(sc->sc_ah);
+
        spin_unlock_bh(&sc->sc_pcu_lock);
 
        if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
@@ -1237,6 +1176,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
+       bool prev_idle;
 
        mutex_lock(&sc->mutex);
 
@@ -1267,35 +1207,45 @@ static void ath9k_stop(struct ieee80211_hw *hw)
         * before setting the invalid flag. */
        ath9k_hw_disable_interrupts(ah);
 
-       if (!(sc->sc_flags & SC_OP_INVALID)) {
-               ath_drain_all_txq(sc, false);
-               ath_stoprecv(sc);
-               ath9k_hw_phy_disable(ah);
-       } else
-               sc->rx.rxlink = NULL;
+       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);
+
+       prev_idle = sc->ps_idle;
+       sc->ps_idle = true;
+
+       spin_lock_bh(&sc->sc_pcu_lock);
+
+       if (ah->led_pin >= 0) {
+               ath9k_hw_set_gpio(ah, ah->led_pin, 1);
+               ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
+       }
+
+       ath_prepare_reset(sc, false, true);
 
        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);
+       if (!ah->curchan)
+               ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
 
-       spin_unlock_bh(&sc->sc_pcu_lock);
+       ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
+       ath9k_hw_phy_disable(ah);
 
-       /* 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_hw_configpcipowersave(ah, true);
 
-       ath9k_ps_restore(sc);
+       spin_unlock_bh(&sc->sc_pcu_lock);
 
-       sc->ps_idle = true;
-       ath_radio_disable(sc, hw);
+       ath9k_ps_restore(sc);
 
        sc->sc_flags |= SC_OP_INVALID;
+       sc->ps_idle = prev_idle;
 
        mutex_unlock(&sc->mutex);
 
@@ -1635,8 +1585,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_conf *conf = &hw->conf;
-       bool disable_radio = false;
 
+       ath9k_ps_wakeup(sc);
        mutex_lock(&sc->mutex);
 
        /*
@@ -1645,16 +1595,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
         * of the changes. Likewise we must only disable the radio towards
         * the end.
         */
-       if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+       if (changed & IEEE80211_CONF_CHANGE_IDLE)
                sc->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE);
-               if (!sc->ps_idle) {
-                       ath_radio_enable(sc, hw);
-                       ath_dbg(common, ATH_DBG_CONFIG,
-                               "not-idle: enabling radio\n");
-               } else {
-                       disable_radio = true;
-               }
-       }
 
        /*
         * We just prepare to enable PS. We have to wait until our AP has
@@ -1760,18 +1702,12 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                ath_dbg(common, ATH_DBG_CONFIG,
                        "Set power: %d\n", conf->power_level);
                sc->config.txpowlimit = 2 * conf->power_level;
-               ath9k_ps_wakeup(sc);
                ath9k_cmn_update_txpow(ah, sc->curtxpow,
                                       sc->config.txpowlimit, &sc->curtxpow);
-               ath9k_ps_restore(sc);
-       }
-
-       if (disable_radio) {
-               ath_dbg(common, ATH_DBG_CONFIG, "idle: disabling radio\n");
-               ath_radio_disable(sc, hw);
        }
 
        mutex_unlock(&sc->mutex);
+       ath9k_ps_restore(sc);
 
        return 0;
 }
index 2dcdf63cb390bfa894c1017972b53b296c2475d9..a439edc5dc06de032575ede2faa1504f04b6afad 100644 (file)
@@ -307,12 +307,11 @@ static int ath_pci_suspend(struct device *device)
        struct ieee80211_hw *hw = pci_get_drvdata(pdev);
        struct ath_softc *sc = hw->priv;
 
-       ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
-
        /* The device has to be moved to FULLSLEEP forcibly.
         * Otherwise the chip never moved to full sleep,
         * when no interface is up.
         */
+       ath9k_hw_disable(sc->sc_ah);
        ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
 
        return 0;
@@ -321,8 +320,6 @@ static int ath_pci_suspend(struct device *device)
 static int ath_pci_resume(struct device *device)
 {
        struct pci_dev *pdev = to_pci_dev(device);
-       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-       struct ath_softc *sc = hw->priv;
        u32 val;
 
        /*
@@ -334,22 +331,6 @@ static int ath_pci_resume(struct device *device)
        if ((val & 0x0000ff00) != 0)
                pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
 
-       ath9k_ps_wakeup(sc);
-       /* Enable LED */
-       ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
-                           AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-       ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
-
-         /*
-          * Reset key cache to sane defaults (all entries cleared) instead of
-          * semi-random values after suspend/resume.
-          */
-       ath9k_cmn_init_crypto(sc->sc_ah);
-       ath9k_ps_restore(sc);
-
-       sc->ps_idle = true;
-       ath_radio_disable(sc, hw);
-
        return 0;
 }