summaryrefslogtreecommitdiffstats
path: root/package/mac80211/patches/582-ath9k_merge_reset_functions.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/mac80211/patches/582-ath9k_merge_reset_functions.patch')
-rw-r--r--package/mac80211/patches/582-ath9k_merge_reset_functions.patch446
1 files changed, 446 insertions, 0 deletions
diff --git a/package/mac80211/patches/582-ath9k_merge_reset_functions.patch b/package/mac80211/patches/582-ath9k_merge_reset_functions.patch
new file mode 100644
index 000000000..5484829b1
--- /dev/null
+++ b/package/mac80211/patches/582-ath9k_merge_reset_functions.patch
@@ -0,0 +1,446 @@
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -212,83 +212,57 @@ static int ath_update_survey_stats(struc
+ return ret;
+ }
+
+-/*
+- * Set/change channels. If the channel is really being changed, it's done
+- * by reseting the chip. To accomplish this we must first cleanup any pending
+- * DMA, then restart stuff.
+-*/
+-static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
+- struct ath9k_channel *hchan)
++static void __ath_cancel_work(struct ath_softc *sc)
+ {
+- struct ath_hw *ah = sc->sc_ah;
+- struct ath_common *common = ath9k_hw_common(ah);
+- struct ieee80211_conf *conf = &common->hw->conf;
+- bool fastcc = true, stopped;
+- struct ieee80211_channel *channel = hw->conf.channel;
+- struct ath9k_hw_cal_data *caldata = NULL;
+- int r;
+-
+- if (sc->sc_flags & SC_OP_INVALID)
+- return -EIO;
+-
+- sc->hw_busy_count = 0;
+-
+- del_timer_sync(&common->ani.timer);
+ 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);
++static void ath_cancel_work(struct ath_softc *sc)
++{
++ __ath_cancel_work(sc);
++ cancel_work_sync(&sc->hw_reset_work);
++}
+
+- spin_lock_bh(&sc->sc_pcu_lock);
++static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
++{
++ struct ath_hw *ah = sc->sc_ah;
++ struct ath_common *common = ath9k_hw_common(ah);
++ bool ret;
+
+- /*
+- * This is only performed if the channel settings have
+- * actually changed.
+- *
+- * To switch channels clear any pending DMA operations;
+- * wait long enough for the RX fifo to drain, reset the
+- * hardware at the new frequency, and then re-enable
+- * the relevant bits of the h/w.
+- */
+- ath9k_hw_disable_interrupts(ah);
+- stopped = ath_drain_all_txq(sc, false);
++ ieee80211_stop_queues(sc->hw);
+
+- if (!ath_stoprecv(sc))
+- stopped = false;
++ sc->hw_busy_count = 0;
++ del_timer_sync(&common->ani.timer);
+
+- if (!ath9k_hw_check_alive(ah))
+- stopped = false;
++ ath9k_hw_disable_interrupts(ah);
+
+- /* XXX: do not flush receive queue here. We don't want
+- * to flush data frames already in queue because of
+- * changing channel. */
++ ret = ath_drain_all_txq(sc, retry_tx);
+
+- if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL))
+- fastcc = false;
++ if (!ath_stoprecv(sc))
++ ret = false;
+
+- if (!(sc->sc_flags & SC_OP_OFFCHANNEL))
+- caldata = &sc->caldata;
++ if (!flush) {
++ if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
++ ath_rx_tasklet(sc, 0, true);
++ ath_rx_tasklet(sc, 0, false);
++ } else {
++ ath_flushrecv(sc);
++ }
+
+- ath_dbg(common, ATH_DBG_CONFIG,
+- "(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n",
+- sc->sc_ah->curchan->channel,
+- channel->center_freq, conf_is_ht40(conf),
+- fastcc);
++ return ret;
++}
+
+- r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
+- if (r) {
+- ath_err(common,
+- "Unable to reset channel (%u MHz), reset status %d\n",
+- channel->center_freq, r);
+- goto ps_restore;
+- }
++static bool ath_complete_reset(struct ath_softc *sc, bool start)
++{
++ struct ath_hw *ah = sc->sc_ah;
++ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (ath_startrecv(sc) != 0) {
+ ath_err(common, "Unable to restart recv logic\n");
+- r = -EIO;
+- goto ps_restore;
++ return false;
+ }
+
+ ath9k_cmn_update_txpow(ah, sc->curtxpow,
+@@ -296,21 +270,93 @@ static int ath_set_channel(struct ath_so
+ ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_enable_interrupts(ah);
+
+- if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) {
++ if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) && start) {
+ if (sc->sc_flags & SC_OP_BEACONS)
+ ath_set_beacon(sc);
++
+ ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+ ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
+ if (!common->disable_ani)
+ ath_start_ani(common);
+ }
+
+- ps_restore:
+- ieee80211_wake_queues(hw);
++ ieee80211_wake_queues(sc->hw);
++
++ return true;
++}
++
++static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
++ bool retry_tx)
++{
++ struct ath_hw *ah = sc->sc_ah;
++ struct ath_common *common = ath9k_hw_common(ah);
++ struct ath9k_hw_cal_data *caldata = NULL;
++ bool fastcc = true;
++ bool flush = false;
++ int r;
++
++ __ath_cancel_work(sc);
++
++ spin_lock_bh(&sc->sc_pcu_lock);
+
++ if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) {
++ fastcc = false;
++ caldata = &sc->caldata;
++ }
++
++ if (!hchan) {
++ fastcc = false;
++ flush = true;
++ hchan = ah->curchan;
++ }
++
++ if (fastcc && !ath9k_hw_check_alive(ah))
++ fastcc = false;
++
++ if (!ath_prepare_reset(sc, retry_tx, flush))
++ fastcc = false;
++
++ ath_dbg(common, ATH_DBG_CONFIG,
++ "Reset to %u MHz, HT40: %d fastcc: %d\n",
++ hchan->channel, !!(hchan->channelFlags & (CHANNEL_HT40MINUS |
++ CHANNEL_HT40PLUS)),
++ fastcc);
++
++ r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
++ if (r) {
++ ath_err(common,
++ "Unable to reset channel, reset status %d\n", r);
++ goto out;
++ }
++
++ if (!ath_complete_reset(sc, true))
++ r = -EIO;
++
++out:
+ spin_unlock_bh(&sc->sc_pcu_lock);
++ return 0;
++}
++
++
++/*
++ * Set/change channels. If the channel is really being changed, it's done
++ * by reseting the chip. To accomplish this we must first cleanup any pending
++ * DMA, then restart stuff.
++*/
++static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
++ struct ath9k_channel *hchan)
++{
++ int r;
++
++ if (sc->sc_flags & SC_OP_INVALID)
++ return -EIO;
++
++ ath9k_ps_wakeup(sc);
++
++ r = ath_reset_internal(sc, hchan, false);
+
+ ath9k_ps_restore(sc);
++
+ return r;
+ }
+
+@@ -823,28 +869,13 @@ static void ath_radio_enable(struct ath_
+ channel->center_freq, r);
+ }
+
+- ath9k_cmn_update_txpow(ah, sc->curtxpow,
+- sc->config.txpowlimit, &sc->curtxpow);
+- if (ath_startrecv(sc) != 0) {
+- ath_err(common, "Unable to restart recv logic\n");
+- goto out;
+- }
+- if (sc->sc_flags & SC_OP_BEACONS)
+- ath_set_beacon(sc); /* restart beacons */
+-
+- /* Re-Enable interrupts */
+- ath9k_hw_set_interrupts(ah, ah->imask);
+- ath9k_hw_enable_interrupts(ah);
++ 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);
+
+- ieee80211_wake_queues(hw);
+- ieee80211_queue_delayed_work(hw, &sc->hw_pll_work, HZ/2);
+-
+-out:
+ spin_unlock_bh(&sc->sc_pcu_lock);
+
+ ath9k_ps_restore(sc);
+@@ -857,11 +888,10 @@ void ath_radio_disable(struct ath_softc
+ int r;
+
+ ath9k_ps_wakeup(sc);
+- cancel_delayed_work_sync(&sc->hw_pll_work);
+
+- spin_lock_bh(&sc->sc_pcu_lock);
++ ath_cancel_work(sc);
+
+- ieee80211_stop_queues(hw);
++ spin_lock_bh(&sc->sc_pcu_lock);
+
+ /*
+ * Keep the LED on when the radio is disabled
+@@ -872,13 +902,7 @@ void ath_radio_disable(struct ath_softc
+ ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
+ }
+
+- /* Disable interrupts */
+- ath9k_hw_disable_interrupts(ah);
+-
+- ath_drain_all_txq(sc, false); /* clear pending tx frames */
+-
+- ath_stoprecv(sc); /* turn off frame recv */
+- ath_flushrecv(sc); /* flush recv queue */
++ ath_prepare_reset(sc, false, true);
+
+ if (!ah->curchan)
+ ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
+@@ -900,48 +924,11 @@ void ath_radio_disable(struct ath_softc
+
+ static int ath_reset(struct ath_softc *sc, bool retry_tx)
+ {
+- struct ath_hw *ah = sc->sc_ah;
+- struct ath_common *common = ath9k_hw_common(ah);
+- struct ieee80211_hw *hw = sc->hw;
+ int r;
+
+- sc->hw_busy_count = 0;
+-
+- /* Stop ANI */
+-
+- del_timer_sync(&common->ani.timer);
+-
+ ath9k_ps_wakeup(sc);
+
+- ieee80211_stop_queues(hw);
+-
+- ath9k_hw_disable_interrupts(ah);
+- ath_drain_all_txq(sc, retry_tx);
+-
+- ath_stoprecv(sc);
+- ath_flushrecv(sc);
+-
+- r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
+- if (r)
+- ath_err(common,
+- "Unable to reset hardware; reset status %d\n", r);
+-
+- if (ath_startrecv(sc) != 0)
+- ath_err(common, "Unable to start recv logic\n");
+-
+- /*
+- * We may be doing a reset in response to a request
+- * that changes the channel so update any state that
+- * might change as a result.
+- */
+- ath9k_cmn_update_txpow(ah, sc->curtxpow,
+- sc->config.txpowlimit, &sc->curtxpow);
+-
+- if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL)))
+- ath_set_beacon(sc); /* restart beacons */
+-
+- ath9k_hw_set_interrupts(ah, ah->imask);
+- ath9k_hw_enable_interrupts(ah);
++ r = ath_reset_internal(sc, NULL, retry_tx);
+
+ if (retry_tx) {
+ int i;
+@@ -954,12 +941,6 @@ static int ath_reset(struct ath_softc *s
+ }
+ }
+
+- ieee80211_wake_queues(hw);
+-
+- /* Start ANI */
+- if (!common->disable_ani)
+- ath_start_ani(common);
+-
+ ath9k_ps_restore(sc);
+
+ return r;
+@@ -969,9 +950,7 @@ void ath_reset_work(struct work_struct *
+ {
+ struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
+
+- spin_lock_bh(&sc->sc_pcu_lock);
+ ath_reset(sc, true);
+- spin_unlock_bh(&sc->sc_pcu_lock);
+ }
+
+ void ath_hw_check(struct work_struct *work)
+@@ -992,11 +971,8 @@ void ath_hw_check(struct work_struct *wo
+ ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, "
+ "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1);
+ if (busy >= 99) {
+- if (++sc->hw_busy_count >= 3) {
+- spin_lock_bh(&sc->sc_pcu_lock);
+- ath_reset(sc, true);
+- spin_unlock_bh(&sc->sc_pcu_lock);
+- }
++ if (++sc->hw_busy_count >= 3)
++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+
+ } else if (busy >= 0)
+ sc->hw_busy_count = 0;
+@@ -1016,9 +992,7 @@ static void ath_hw_pll_rx_hang_check(str
+ /* Rx is hung for more than 500ms. Reset it */
+ ath_dbg(common, ATH_DBG_RESET,
+ "Possible RX hang, resetting");
+- spin_lock_bh(&sc->sc_pcu_lock);
+- ath_reset(sc, true);
+- spin_unlock_bh(&sc->sc_pcu_lock);
++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ count = 0;
+ }
+ } else
+@@ -1089,28 +1063,6 @@ static int ath9k_start(struct ieee80211_
+ goto mutex_unlock;
+ }
+
+- /*
+- * This is needed only to setup initial state
+- * but it's best done after a reset.
+- */
+- ath9k_cmn_update_txpow(ah, sc->curtxpow,
+- sc->config.txpowlimit, &sc->curtxpow);
+-
+- /*
+- * Setup the hardware after reset:
+- * The receive engine is set going.
+- * Frame transmit is handled entirely
+- * in the frame output path; there's nothing to do
+- * here except setup the interrupt mask.
+- */
+- if (ath_startrecv(sc) != 0) {
+- ath_err(common, "Unable to start recv logic\n");
+- r = -EIO;
+- spin_unlock_bh(&sc->sc_pcu_lock);
+- goto mutex_unlock;
+- }
+- spin_unlock_bh(&sc->sc_pcu_lock);
+-
+ /* Setup our intr mask. */
+ ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
+ ATH9K_INT_RXORN | ATH9K_INT_FATAL |
+@@ -1133,12 +1085,14 @@ static int ath9k_start(struct ieee80211_
+
+ /* Disable BMISS interrupt when we're not associated */
+ ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
+- ath9k_hw_set_interrupts(ah, ah->imask);
+- ath9k_hw_enable_interrupts(ah);
+
+- ieee80211_wake_queues(hw);
++ if (!ath_complete_reset(sc, false)) {
++ r = -EIO;
++ spin_unlock_bh(&sc->sc_pcu_lock);
++ goto mutex_unlock;
++ }
+
+- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
++ spin_unlock_bh(&sc->sc_pcu_lock);
+
+ if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
+ !ah->btcoex_hw.enabled) {
+@@ -1231,10 +1185,7 @@ static void ath9k_stop(struct ieee80211_
+
+ mutex_lock(&sc->mutex);
+
+- 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);
++ ath_cancel_work(sc);
+
+ if (sc->sc_flags & SC_OP_INVALID) {
+ ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
+@@ -2351,9 +2302,11 @@ static void ath9k_flush(struct ieee80211
+ 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);
+- spin_unlock_bh(&sc->sc_pcu_lock);
++
+ ath9k_ps_restore(sc);
+ ieee80211_wake_queues(hw);
+