From: Felix Fietkau Date: Mon, 11 Nov 2013 21:23:33 +0000 (+0100) Subject: ath9k: use a timer to put hardware into full sleep X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=bf3dac5a6fcf33a80c99e7b7406f8e5af99e2a70;p=linux-beck.git ath9k: use a timer to put hardware into full sleep When operating in client mode, the short period of time between scanning and associating is often enough to put the hardware through several FULL-SLEEP <-> AWAKE transitions, each wakeup requiring a reset to fully recover the hardware. This is completely unnecessary and can easily be avoided by deferring the switch to full sleep. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index e1013425cc7f..0783e50f44b2 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -459,6 +459,7 @@ void ath_check_ani(struct ath_softc *sc); int ath_update_survey_stats(struct ath_softc *sc); void ath_update_survey_nf(struct ath_softc *sc, int channel); void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type); +void ath_ps_full_sleep(unsigned long data); /**********/ /* BTCOEX */ @@ -789,6 +790,7 @@ struct ath_softc { struct delayed_work tx_complete_work; struct delayed_work hw_pll_work; struct timer_list rx_poll_timer; + struct timer_list sleep_timer; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex btcoex; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index fb55302c958d..65a2add3b676 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -738,6 +738,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, (unsigned long)sc); + setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc); 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); @@ -1049,6 +1050,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); + del_timer_sync(&sc->sleep_timer); ath9k_hw_deinit(sc->sc_ah); if (sc->dfs_detector != NULL) sc->dfs_detector->exit(sc->dfs_detector); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e54ffdd36e21..f86404a70a73 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -82,6 +82,22 @@ static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) return ret; } +void ath_ps_full_sleep(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *) data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + bool reset; + + spin_lock(&common->cc_lock); + ath_hw_cycle_counters_update(common); + spin_unlock(&common->cc_lock); + + ath9k_hw_setrxabort(sc->sc_ah, 1); + ath9k_hw_stopdmarecv(sc->sc_ah, &reset); + + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); +} + void ath9k_ps_wakeup(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -92,6 +108,7 @@ void ath9k_ps_wakeup(struct ath_softc *sc) if (++sc->ps_usecount != 1) goto unlock; + del_timer_sync(&sc->sleep_timer); power_mode = sc->sc_ah->power_mode; ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); @@ -117,17 +134,17 @@ 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; - bool reset; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (--sc->ps_usecount != 0) goto unlock; if (sc->ps_idle) { - ath9k_hw_setrxabort(sc->sc_ah, 1); - ath9k_hw_stopdmarecv(sc->sc_ah, &reset); - mode = ATH9K_PM_FULL_SLEEP; - } else if (sc->ps_enabled && + mod_timer(&sc->sleep_timer, jiffies + HZ / 10); + goto unlock; + } + + if (sc->ps_enabled && !(sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA |