From 2201e32df32f7a0ab110cab57cf2b228743f2d78 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 5 Nov 2011 19:51:32 +0000 Subject: [PATCH] ath9k: add some fixes for radio reinit and frame flush SVN-Revision: 28772 --- .../patches/560-ath9k_fix_radio_stop.patch | 275 ++++++++++++++++++ .../patches/561-ath9k_fix_flush.patch | 45 +++ 2 files changed, 320 insertions(+) create mode 100644 package/mac80211/patches/560-ath9k_fix_radio_stop.patch create mode 100644 package/mac80211/patches/561-ath9k_fix_flush.patch diff --git a/package/mac80211/patches/560-ath9k_fix_radio_stop.patch b/package/mac80211/patches/560-ath9k_fix_radio_stop.patch new file mode 100644 index 000000000000..029f1e4904dd --- /dev/null +++ b/package/mac80211/patches/560-ath9k_fix_radio_stop.patch @@ -0,0 +1,275 @@ +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -124,14 +124,14 @@ void ath9k_ps_restore(struct ath_softc * + !(sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | +- PS_WAIT_FOR_TX_ACK))) ++ PS_WAIT_FOR_TX_ACK))) { + mode = ATH9K_PM_NETWORK_SLEEP; +- else +- goto unlock; + +- spin_lock(&common->cc_lock); +- ath_hw_cycle_counters_update(common); +- spin_unlock(&common->cc_lock); ++ spin_lock(&common->cc_lock); ++ ath_hw_cycle_counters_update(common); ++ spin_unlock(&common->cc_lock); ++ } else ++ goto unlock; + + ath9k_hw_setpower(sc->sc_ah, mode); + +@@ -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) && +@@ -1260,9 +1197,6 @@ static void ath9k_stop(struct ieee80211_ + sc->rx.frag = NULL; + } + +- /* disable HAL and put h/w to sleep */ +- ath9k_hw_disable(ah); +- + spin_unlock_bh(&sc->sc_pcu_lock); + + /* we can now sync irq and kill any running tasklets, since we already +@@ -1271,10 +1205,27 @@ static void ath9k_stop(struct ieee80211_ + tasklet_kill(&sc->intr_tq); + tasklet_kill(&sc->bcon_tasklet); + +- ath9k_ps_restore(sc); +- + sc->ps_idle = true; +- ath_radio_disable(sc, hw); ++ ++ 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 (!ah->curchan) ++ ah->curchan = ath9k_cmn_get_curchannel(hw, ah); ++ ++ ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); ++ ath9k_hw_phy_disable(ah); ++ ath9k_hw_disable(ah); ++ ++ ath9k_hw_configpcipowersave(ah, true); ++ ++ spin_unlock_bh(&sc->sc_pcu_lock); ++ ++ ath9k_ps_restore(sc); + + sc->sc_flags |= SC_OP_INVALID; + +@@ -1598,8 +1549,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 +1559,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 +1685,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 +@@ -302,26 +302,12 @@ static void ath_pci_remove(struct pci_de + + static int ath_pci_suspend(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; +- +- 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_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); +- + return 0; + } + + 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; + + /* +@@ -333,22 +319,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/561-ath9k_fix_flush.patch b/package/mac80211/patches/561-ath9k_fix_flush.patch new file mode 100644 index 000000000000..58b71201a7f6 --- /dev/null +++ b/package/mac80211/patches/561-ath9k_fix_flush.patch @@ -0,0 +1,45 @@ +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -2259,9 +2259,6 @@ static void ath9k_flush(struct ieee80211 + return; + } + +- if (drop) +- timeout = 1; +- + for (j = 0; j < timeout; j++) { + bool npend = false; + +@@ -2279,21 +2276,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); + } -- 2.30.2