diff options
Diffstat (limited to 'package')
| -rw-r--r-- | package/mac80211/patches/570-ath9k_bb_hang_check_fix.patch | 109 | 
1 files changed, 109 insertions, 0 deletions
| diff --git a/package/mac80211/patches/570-ath9k_bb_hang_check_fix.patch b/package/mac80211/patches/570-ath9k_bb_hang_check_fix.patch new file mode 100644 index 000000000..5314883fc --- /dev/null +++ b/package/mac80211/patches/570-ath9k_bb_hang_check_fix.patch @@ -0,0 +1,109 @@ +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -177,7 +177,12 @@ static void ath_update_survey_nf(struct  + 	} + } +  +-static void ath_update_survey_stats(struct ath_softc *sc) ++/* ++ * Updates the survey statistics and returns the busy time since last ++ * update in %, if the measurement duration was long enough for the ++ * result to be useful, -1 otherwise. ++ */ ++static int ath_update_survey_stats(struct ath_softc *sc) + { + 	struct ath_hw *ah = sc->sc_ah; + 	struct ath_common *common = ath9k_hw_common(ah); +@@ -185,9 +190,10 @@ static void ath_update_survey_stats(stru + 	struct survey_info *survey = &sc->survey[pos]; + 	struct ath_cycle_counters *cc = &common->cc_survey; + 	unsigned int div = common->clockrate * 1000; ++	int ret = 0; +  + 	if (!ah->curchan) +-		return; ++		return -1; +  + 	if (ah->power_mode == ATH9K_PM_AWAKE) + 		ath_hw_cycle_counters_update(common); +@@ -202,9 +208,18 @@ static void ath_update_survey_stats(stru + 		survey->channel_time_rx += cc->rx_frame / div; + 		survey->channel_time_tx += cc->tx_frame / div; + 	} ++ ++	if (cc->cycles < div) ++		return -1; ++ ++	if (cc->cycles > 0) ++		ret = cc->rx_busy * 100 / cc->cycles; ++ + 	memset(cc, 0, sizeof(*cc)); +  + 	ath_update_survey_nf(sc, pos); ++ ++	return ret; + } +  + /* +@@ -226,6 +241,8 @@ int ath_set_channel(struct ath_softc *sc + 	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); +@@ -584,17 +601,25 @@ static void ath_node_detach(struct ath_s + void ath_hw_check(struct work_struct *work) + { + 	struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); +-	int i; ++	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; +  +-	for (i = 0; i < 3; i++) { +-		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); +  +-		msleep(1); +-	} +-	ath_reset(sc, true); ++	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) ++			ath_reset(sc, true); ++	} else if (busy >= 0) ++		sc->hw_busy_count = 0; +  + out: + 	ath9k_ps_restore(sc); +@@ -988,6 +1013,8 @@ int ath_reset(struct ath_softc *sc, bool + 	struct ieee80211_hw *hw = sc->hw; + 	int r; +  ++	sc->hw_busy_count = 0; ++ + 	/* Stop ANI */ + 	del_timer_sync(&common->ani.timer); +  +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -598,6 +598,8 @@ struct ath_softc { + 	struct completion paprd_complete; + 	bool paprd_pending; +  ++	unsigned int hw_busy_count; ++ + 	u32 intrstatus; + 	u32 sc_flags; /* SC_OP_* */ + 	u16 ps_flags; /* PS_* */ | 
