Message ID | 20161114144226.15748-1-sven.eckelmann@open-mesh.com (mailing list archive) |
---|---|
State | RFC |
Delegated to: | Kalle Valo |
Headers | show |
Sven Eckelmann <sven.eckelmann@open-mesh.com> writes: > From: Simon Wunderlich <simon.wunderlich@open-mesh.com> > > QCA 802.11n chips (especially AR9330/AR9340) sometimes end up in a state in > which a read of AR_CFG always returns 0xdeadbeef. This should not happen > when when the power_mode of the device is ATH9K_PM_AWAKE. > > This problem is not yet detected by any other workaround in ath9k. No way > is known to reproduce the problem easily. > > Signed-off-by: Simon Wunderlich <simon.wunderlich@open-mesh.com> > [sven.eckelmann@open-mesh.com: port to recent ath9k, add commit message] > Signed-off-by: Sven Eckelmann <sven.eckelmann@open-mesh.com> [...] > +void ath_hw_hang_work(struct work_struct *work) > +{ > + struct ath_softc *sc = container_of(work, struct ath_softc, > + hw_hang_work.work); > + > + if (ath_hw_hang_deadbeef(sc)) > + goto requeue_worker; > + > +requeue_worker: > + ieee80211_queue_delayed_work(sc->hw, &sc->hw_hang_work, > + msecs_to_jiffies(ATH_HANG_WORK_INTERVAL)); > +} The goto doesn't make any sense, either me or the function is missing something :)
On Mittwoch, 16. November 2016 16:16:42 CET Valo, Kalle wrote: > Sven Eckelmann <sven.eckelmann@open-mesh.com> writes: > > > From: Simon Wunderlich <simon.wunderlich@open-mesh.com> > > > > QCA 802.11n chips (especially AR9330/AR9340) sometimes end up in a state in > > which a read of AR_CFG always returns 0xdeadbeef. This should not happen > > when when the power_mode of the device is ATH9K_PM_AWAKE. > > > > This problem is not yet detected by any other workaround in ath9k. No way > > is known to reproduce the problem easily. > > > > Signed-off-by: Simon Wunderlich <simon.wunderlich@open-mesh.com> > > [sven.eckelmann@open-mesh.com: port to recent ath9k, add commit message] > > Signed-off-by: Sven Eckelmann <sven.eckelmann@open-mesh.com> > > [...] > > > +void ath_hw_hang_work(struct work_struct *work) > > +{ > > + struct ath_softc *sc = container_of(work, struct ath_softc, > > + hw_hang_work.work); > > + > > + if (ath_hw_hang_deadbeef(sc)) > > + goto requeue_worker; > > + > > +requeue_worker: > > + ieee80211_queue_delayed_work(sc->hw, &sc->hw_hang_work, > > + msecs_to_jiffies(ATH_HANG_WORK_INTERVAL)); > > +} > > The goto doesn't make any sense, either me or the function is missing > something :) > > It is just for the next patch. And yes, could be done differently. Kind regards, Sven
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 26fc8ec..9c6fee7 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -710,11 +710,13 @@ void ath9k_csa_update(struct ath_softc *sc); #define ATH_ANI_MAX_SKIP_COUNT 10 #define ATH_PAPRD_TIMEOUT 100 /* msecs */ #define ATH_PLL_WORK_INTERVAL 100 +#define ATH_HANG_WORK_INTERVAL 30000 void ath_tx_complete_poll_work(struct work_struct *work); void ath_reset_work(struct work_struct *work); bool ath_hw_check(struct ath_softc *sc); void ath_hw_pll_work(struct work_struct *work); +void ath_hw_hang_work(struct work_struct *work); void ath_paprd_calibrate(struct work_struct *work); void ath_ani_calibrate(unsigned long data); void ath_start_ani(struct ath_softc *sc); @@ -1014,6 +1016,7 @@ struct ath_softc { #endif struct delayed_work tx_complete_work; struct delayed_work hw_pll_work; + struct delayed_work hw_hang_work; struct timer_list sleep_timer; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index c56e40f..608b370 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -767,6 +767,7 @@ static int read_file_reset(struct seq_file *file, void *data) [RESET_TYPE_CALIBRATION] = "Calibration error", [RESET_TX_DMA_ERROR] = "Tx DMA stop error", [RESET_RX_DMA_ERROR] = "Rx DMA stop error", + [RESET_TYPE_DEADBEEF] = "deadbeef hang", }; int i; diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index cd68c5f..0d77abbf6 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -52,6 +52,7 @@ enum ath_reset_type { RESET_TYPE_CALIBRATION, RESET_TX_DMA_ERROR, RESET_RX_DMA_ERROR, + RESET_TYPE_DEADBEEF, __RESET_TYPE_MAX }; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 368d9b3..9bc7d1c 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -638,6 +638,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, INIT_WORK(&sc->hw_reset_work, ath_reset_work); INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); + INIT_DELAYED_WORK(&sc->hw_hang_work, ath_hw_hang_work); ath9k_init_channel_context(sc); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 5ad0fee..ff11d85 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -138,6 +138,39 @@ void ath_hw_pll_work(struct work_struct *work) msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); } +static bool ath_hw_hang_deadbeef(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + u32 reg; + + /* check for stucked MAC */ + ath9k_ps_wakeup(sc); + reg = REG_READ(sc->sc_ah, AR_CFG); + ath9k_ps_restore(sc); + + if (reg != 0xdeadbeef) + return false; + + ath_dbg(common, RESET, + "0xdeadbeef hang is detected. Schedule chip reset\n"); + ath9k_queue_reset(sc, RESET_TYPE_DEADBEEF); + + return true; +} + +void ath_hw_hang_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + hw_hang_work.work); + + if (ath_hw_hang_deadbeef(sc)) + goto requeue_worker; + +requeue_worker: + ieee80211_queue_delayed_work(sc->hw, &sc->hw_hang_work, + msecs_to_jiffies(ATH_HANG_WORK_INTERVAL)); +} + /* * PA Pre-distortion. */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e9f32b5..4d3e216 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -183,6 +183,7 @@ static void __ath_cancel_work(struct ath_softc *sc) cancel_work_sync(&sc->paprd_work); cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->hw_pll_work); + cancel_delayed_work_sync(&sc->hw_hang_work); #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT if (ath9k_hw_mci_is_enabled(sc->sc_ah)) @@ -204,6 +205,9 @@ void ath_restart_work(struct ath_softc *sc) ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); + ieee80211_queue_delayed_work(sc->hw, &sc->hw_hang_work, + msecs_to_jiffies(ATH_HANG_WORK_INTERVAL)); + ath_start_ani(sc); }