From: Felix Fietkau Date: Tue, 15 Nov 2011 14:53:21 +0000 (+0000) Subject: ath9k: reorganize patches, reset hardware after full sleep (fixes #10349) X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=commitdiff_plain;hb=f51571fb7fe6f237b5b7ef2df6c40abfcb9e2a1e ath9k: reorganize patches, reset hardware after full sleep (fixes #10349) SVN-Revision: 29155 --- diff --git a/package/mac80211/patches/560-ath9k_fix_radio_stop.patch b/package/mac80211/patches/560-ath9k_fix_radio_stop.patch deleted file mode 100644 index 2367ffa6ab..0000000000 --- a/package/mac80211/patches/560-ath9k_fix_radio_stop.patch +++ /dev/null @@ -1,257 +0,0 @@ ---- a/drivers/net/wireless/ath/ath9k/main.c -+++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -880,82 +880,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; -@@ -1091,6 +1015,9 @@ static int ath9k_start(struct ieee80211_ - * 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, -@@ -1129,6 +1056,16 @@ static int ath9k_start(struct ieee80211_ - goto mutex_unlock; - } - -+ 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) && -@@ -1248,33 +1185,39 @@ static void ath9k_stop(struct ieee80211_ - * 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); -+ -+ sc->ps_idle = true; -+ -+ spin_lock_bh(&sc->sc_pcu_lock); -+ -+ 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; - -@@ -1598,8 +1541,8 @@ static int ath9k_config(struct ieee80211 - 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); - - /* -@@ -1608,16 +1551,8 @@ static int ath9k_config(struct ieee80211 - * 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 -@@ -1742,19 +1677,13 @@ static int ath9k_config(struct ieee80211 - 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); - conf->cur_power_level = sc->curtxpow / 2; - } - -- 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; - } ---- a/drivers/net/wireless/ath/ath9k/pci.c -+++ b/drivers/net/wireless/ath/ath9k/pci.c -@@ -306,12 +306,11 @@ static int ath_pci_suspend(struct 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; -@@ -333,22 +332,6 @@ static int ath_pci_resume(struct 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; - } - diff --git a/package/mac80211/patches/560-ath9k_reset_after_full_sleep.patch b/package/mac80211/patches/560-ath9k_reset_after_full_sleep.patch new file mode 100644 index 0000000000..9a5b580019 --- /dev/null +++ b/package/mac80211/patches/560-ath9k_reset_after_full_sleep.patch @@ -0,0 +1,68 @@ +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -619,6 +619,7 @@ struct ath_softc { + u16 curtxpow; + bool ps_enabled; + bool ps_idle; ++ bool ps_fullsleep; + short nbcnvifs; + short nvifs; + unsigned long ps_usecount; +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -118,9 +118,10 @@ void ath9k_ps_restore(struct ath_softc * + if (--sc->ps_usecount != 0) + goto unlock; + +- if (sc->ps_idle) ++ if (sc->ps_idle && (sc->ps_flags & PS_WAIT_FOR_TX_ACK)) { + mode = ATH9K_PM_FULL_SLEEP; +- else if (sc->ps_enabled && ++ sc->ps_fullsleep = true; ++ } else if (sc->ps_enabled && + !(sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | +@@ -275,6 +276,7 @@ static bool ath_complete_reset(struct at + sc->config.txpowlimit, &sc->curtxpow); + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); ++ sc->ps_fullsleep = false; + + if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) && start) { + if (sc->sc_flags & SC_OP_BEACONS) +@@ -332,7 +334,8 @@ static int ath_reset_internal(struct ath + hchan = ah->curchan; + } + +- if (fastcc && !ath9k_hw_check_alive(ah)) ++ if (fastcc && (sc->ps_fullsleep || ++ !ath9k_hw_check_alive(ah))) + fastcc = false; + + if (!ath_prepare_reset(sc, retry_tx, flush)) +@@ -1173,6 +1176,13 @@ static void ath9k_tx(struct ieee80211_hw + } + } + ++ /* ++ * Cannot tx while the hardware is in full sleep, it first needs a full ++ * chip reset to recover from that ++ */ ++ if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_FULL_SLEEP)) ++ goto exit; ++ + if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) { + /* + * We are using PS-Poll and mac80211 can request TX while in +--- a/drivers/net/wireless/ath/ath9k/xmit.c ++++ b/drivers/net/wireless/ath/ath9k/xmit.c +@@ -1983,7 +1983,7 @@ static void ath_tx_complete(struct ath_s + skb_pull(skb, padsize); + } + +- if (sc->ps_flags & PS_WAIT_FOR_TX_ACK) { ++ if ((sc->ps_flags & PS_WAIT_FOR_TX_ACK) && !txq->axq_depth) { + sc->ps_flags &= ~PS_WAIT_FOR_TX_ACK; + ath_dbg(common, ATH_DBG_PS, + "Going back to sleep after having received TX status (0x%lx)\n", diff --git a/package/mac80211/patches/561-ath9k_fix_flush.patch b/package/mac80211/patches/561-ath9k_fix_flush.patch deleted file mode 100644 index df20890603..0000000000 --- a/package/mac80211/patches/561-ath9k_fix_flush.patch +++ /dev/null @@ -1,45 +0,0 @@ ---- a/drivers/net/wireless/ath/ath9k/main.c -+++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -2251,9 +2251,6 @@ static void ath9k_flush(struct ieee80211 - return; - } - -- if (drop) -- timeout = 1; -- - for (j = 0; j < timeout; j++) { - bool npend = false; - -@@ -2271,21 +2268,22 @@ static void ath9k_flush(struct ieee80211 - } - - if (!npend) -- goto out; -+ break; - } - -- 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 (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); -+ if (!drain_txq) -+ ath_reset(sc, false); - -- ath9k_ps_restore(sc); -- ieee80211_wake_queues(hw); -+ ath9k_ps_restore(sc); -+ ieee80211_wake_queues(hw); -+ } - --out: - ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0); - mutex_unlock(&sc->mutex); - } diff --git a/package/mac80211/patches/561-ath9k_fix_ps_idle_handling.patch b/package/mac80211/patches/561-ath9k_fix_ps_idle_handling.patch new file mode 100644 index 0000000000..40f24e1eca --- /dev/null +++ b/package/mac80211/patches/561-ath9k_fix_ps_idle_handling.patch @@ -0,0 +1,273 @@ +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -883,82 +883,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; +@@ -1094,6 +1018,9 @@ static int ath9k_start(struct ieee80211_ + * 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, +@@ -1132,6 +1059,18 @@ static int ath9k_start(struct ieee80211_ + 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) && +@@ -1229,6 +1168,7 @@ static void ath9k_stop(struct ieee80211_ + 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); + +@@ -1258,35 +1198,45 @@ static void ath9k_stop(struct ieee80211_ + * 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); + +@@ -1608,8 +1558,8 @@ static int ath9k_config(struct ieee80211 + 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); + + /* +@@ -1618,16 +1568,8 @@ static int ath9k_config(struct ieee80211 + * 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 +@@ -1752,19 +1694,13 @@ static int ath9k_config(struct ieee80211 + 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); + conf->cur_power_level = sc->curtxpow / 2; + } + +- 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; + } +--- a/drivers/net/wireless/ath/ath9k/pci.c ++++ b/drivers/net/wireless/ath/ath9k/pci.c +@@ -306,12 +306,11 @@ static int ath_pci_suspend(struct 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; +@@ -333,22 +332,6 @@ static int ath_pci_resume(struct 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; + } + diff --git a/package/mac80211/patches/562-ath9k_fix_flush.patch b/package/mac80211/patches/562-ath9k_fix_flush.patch new file mode 100644 index 0000000000..a5329adc96 --- /dev/null +++ b/package/mac80211/patches/562-ath9k_fix_flush.patch @@ -0,0 +1,45 @@ +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -2268,9 +2268,6 @@ static void ath9k_flush(struct ieee80211 + return; + } + +- if (drop) +- timeout = 1; +- + for (j = 0; j < timeout; j++) { + bool npend = false; + +@@ -2288,21 +2285,22 @@ static void ath9k_flush(struct ieee80211 + } + + if (!npend) +- goto out; ++ break; + } + +- 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 (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); ++ if (!drain_txq) ++ ath_reset(sc, false); + +- ath9k_ps_restore(sc); +- ieee80211_wake_queues(hw); ++ ath9k_ps_restore(sc); ++ ieee80211_wake_queues(hw); ++ } + +-out: + ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0); + mutex_unlock(&sc->mutex); + } diff --git a/package/mac80211/patches/562-ath9k_fix_led.patch b/package/mac80211/patches/562-ath9k_fix_led.patch deleted file mode 100644 index 0ef16684b2..0000000000 --- a/package/mac80211/patches/562-ath9k_fix_led.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- a/drivers/net/wireless/ath/ath9k/main.c -+++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -1056,9 +1056,11 @@ static int ath9k_start(struct ieee80211_ - goto mutex_unlock; - } - -- ath9k_hw_cfg_output(ah, ah->led_pin, -- AR_GPIO_OUTPUT_MUX_AS_OUTPUT); -- ath9k_hw_set_gpio(ah, ah->led_pin, 0); -+ 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 -@@ -1197,8 +1199,10 @@ static void ath9k_stop(struct ieee80211_ - - spin_lock_bh(&sc->sc_pcu_lock); - -- ath9k_hw_set_gpio(ah, ah->led_pin, 1); -- ath9k_hw_cfg_gpio_input(ah, ah->led_pin); -+ 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); - diff --git a/package/mac80211/patches/563-ath9k_fix_full_sleep_tx.patch b/package/mac80211/patches/563-ath9k_fix_full_sleep_tx.patch deleted file mode 100644 index 17575b9e47..0000000000 --- a/package/mac80211/patches/563-ath9k_fix_full_sleep_tx.patch +++ /dev/null @@ -1,22 +0,0 @@ ---- a/drivers/net/wireless/ath/ath9k/main.c -+++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -118,7 +118,7 @@ void ath9k_ps_restore(struct ath_softc * - if (--sc->ps_usecount != 0) - goto unlock; - -- if (sc->ps_idle) -+ if (sc->ps_idle && !(sc->ps_flags & PS_WAIT_FOR_TX_ACK)) - mode = ATH9K_PM_FULL_SLEEP; - else if (sc->ps_enabled && - !(sc->ps_flags & (PS_WAIT_FOR_BEACON | ---- a/drivers/net/wireless/ath/ath9k/xmit.c -+++ b/drivers/net/wireless/ath/ath9k/xmit.c -@@ -1983,7 +1983,7 @@ static void ath_tx_complete(struct ath_s - skb_pull(skb, padsize); - } - -- if (sc->ps_flags & PS_WAIT_FOR_TX_ACK) { -+ if ((sc->ps_flags & PS_WAIT_FOR_TX_ACK) && !txq->axq_depth) { - sc->ps_flags &= ~PS_WAIT_FOR_TX_ACK; - ath_dbg(common, ATH_DBG_PS, - "Going back to sleep after having received TX status (0x%lx)\n",