From d8083fae30589c5fddeb12ba8bf2058594bf6763 Mon Sep 17 00:00:00 2001 From: nbd Date: Mon, 29 Aug 2011 18:41:18 +0000 Subject: ath9k: fix a few crash issues on hardware reset git-svn-id: svn://svn.openwrt.org/openwrt/trunk@28122 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../patches/581-ath9k_use_reset_work.patch | 253 +++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 package/mac80211/patches/581-ath9k_use_reset_work.patch (limited to 'package/mac80211/patches/581-ath9k_use_reset_work.patch') diff --git a/package/mac80211/patches/581-ath9k_use_reset_work.patch b/package/mac80211/patches/581-ath9k_use_reset_work.patch new file mode 100644 index 000000000..db8d85aee --- /dev/null +++ b/package/mac80211/patches/581-ath9k_use_reset_work.patch @@ -0,0 +1,253 @@ +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -429,6 +429,7 @@ void ath9k_set_beaconing_status(struct a + + #define ATH_PAPRD_TIMEOUT 100 /* msecs */ + ++void ath_reset_work(struct work_struct *work); + void ath_hw_check(struct work_struct *work); + void ath_hw_pll_work(struct work_struct *work); + void ath_paprd_calibrate(struct work_struct *work); +@@ -609,6 +610,7 @@ struct ath_softc { + struct mutex mutex; + struct work_struct paprd_work; + struct work_struct hw_check_work; ++ struct work_struct hw_reset_work; + struct completion paprd_complete; + + unsigned int hw_busy_count; +@@ -655,7 +657,6 @@ struct ath_softc { + }; + + void ath9k_tasklet(unsigned long data); +-int ath_reset(struct ath_softc *sc, bool retry_tx); + int ath_cabq_update(struct ath_softc *); + + static inline void ath_read_cachesize(struct ath_common *common, int *csz) +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -776,6 +776,7 @@ int ath9k_init_device(u16 devid, struct + goto error_world; + } + ++ 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); + INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -595,74 +595,6 @@ static void ath_node_detach(struct ath_s + ath_tx_node_cleanup(sc, an); + } + +-void ath_hw_check(struct work_struct *work) +-{ +- struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); +- struct ath_common *common = ath9k_hw_common(sc->sc_ah); +- unsigned long flags; +- int busy; +- +- ath9k_ps_wakeup(sc); +- if (ath9k_hw_check_alive(sc->sc_ah)) +- goto out; +- +- spin_lock_irqsave(&common->cc_lock, flags); +- busy = ath_update_survey_stats(sc); +- spin_unlock_irqrestore(&common->cc_lock, flags); +- +- 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); +- } +- } else if (busy >= 0) +- sc->hw_busy_count = 0; +- +-out: +- ath9k_ps_restore(sc); +-} +- +-static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) +-{ +- static int count; +- struct ath_common *common = ath9k_hw_common(sc->sc_ah); +- +- if (pll_sqsum >= 0x40000) { +- count++; +- if (count == 3) { +- /* 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); +- count = 0; +- } +- } else +- count = 0; +-} +- +-void ath_hw_pll_work(struct work_struct *work) +-{ +- struct ath_softc *sc = container_of(work, struct ath_softc, +- hw_pll_work.work); +- u32 pll_sqsum; +- +- if (AR_SREV_9485(sc->sc_ah)) { +- +- ath9k_ps_wakeup(sc); +- pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); +- ath9k_ps_restore(sc); +- +- ath_hw_pll_rx_hang_check(sc, pll_sqsum); +- +- ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5); +- } +-} +- + + void ath9k_tasklet(unsigned long data) + { +@@ -675,9 +607,7 @@ void ath9k_tasklet(unsigned long data) + + if ((status & ATH9K_INT_FATAL) || + (status & ATH9K_INT_BB_WATCHDOG)) { +- spin_lock(&sc->sc_pcu_lock); +- ath_reset(sc, true); +- spin_unlock(&sc->sc_pcu_lock); ++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work); + return; + } + +@@ -968,7 +898,7 @@ void ath_radio_disable(struct ath_softc + ath9k_ps_restore(sc); + } + +-int ath_reset(struct ath_softc *sc, bool retry_tx) ++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); +@@ -1035,6 +965,84 @@ int ath_reset(struct ath_softc *sc, bool + return r; + } + ++void ath_reset_work(struct work_struct *work) ++{ ++ 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) ++{ ++ struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); ++ struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++ unsigned long flags; ++ int busy; ++ ++ ath9k_ps_wakeup(sc); ++ if (ath9k_hw_check_alive(sc->sc_ah)) ++ goto out; ++ ++ spin_lock_irqsave(&common->cc_lock, flags); ++ busy = ath_update_survey_stats(sc); ++ spin_unlock_irqrestore(&common->cc_lock, flags); ++ ++ 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); ++ } ++ ++ } else if (busy >= 0) ++ sc->hw_busy_count = 0; ++ ++out: ++ ath9k_ps_restore(sc); ++} ++ ++static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) ++{ ++ static int count; ++ struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++ ++ if (pll_sqsum >= 0x40000) { ++ count++; ++ if (count == 3) { ++ /* 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); ++ count = 0; ++ } ++ } else ++ count = 0; ++} ++ ++void ath_hw_pll_work(struct work_struct *work) ++{ ++ struct ath_softc *sc = container_of(work, struct ath_softc, ++ hw_pll_work.work); ++ u32 pll_sqsum; ++ ++ if (AR_SREV_9485(sc->sc_ah)) { ++ ++ ath9k_ps_wakeup(sc); ++ pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); ++ ath9k_ps_restore(sc); ++ ++ ath_hw_pll_rx_hang_check(sc, pll_sqsum); ++ ++ ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5); ++ } ++} ++ + /**********************/ + /* mac80211 callbacks */ + /**********************/ +--- a/drivers/net/wireless/ath/ath9k/xmit.c ++++ b/drivers/net/wireless/ath/ath9k/xmit.c +@@ -601,7 +601,7 @@ static void ath_tx_complete_aggr(struct + rcu_read_unlock(); + + if (needreset) +- ath_reset(sc, false); ++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work); + } + + static bool ath_lookup_legacy(struct ath_buf *bf) +@@ -2268,9 +2268,7 @@ static void ath_tx_complete_poll_work(st + if (needreset) { + ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET, + "tx hung, resetting the chip\n"); +- 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); + } + + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, +--- a/drivers/net/wireless/ath/ath9k/beacon.c ++++ b/drivers/net/wireless/ath/ath9k/beacon.c +@@ -386,9 +386,7 @@ void ath_beacon_tasklet(unsigned long da + ath_dbg(common, ATH_DBG_BSTUCK, + "beacon is officially stuck\n"); + sc->sc_flags |= SC_OP_TSF_RESET; +- spin_lock(&sc->sc_pcu_lock); +- ath_reset(sc, true); +- spin_unlock(&sc->sc_pcu_lock); ++ ieee80211_queue_work(sc->hw, &sc->hw_reset_work); + } + + return; -- cgit v1.2.3