Issam Hamdi <ih@xxxxxxxxxxxxxxxxxx> writes: > From: Simon Wunderlich <simon.wunderlich@xxxxxxxxxxxxx> > > 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. > > This patch originally developed by "Simon Wunderlich <simon.wunderlich@xxxxxxxxxxxxx>" > and "Sven Eckelmann <sven.eckelmann@xxxxxxxxxxxxx>" > > Co-developed-by: Simon Wunderlich <sw@xxxxxxxxxxxxxxxxxx> > Co-developed-by: Sven Eckelmann <se@xxxxxxxxxxxxxxxxxx> > Signed-off-by: Issam Hamdi <ih@xxxxxxxxxxxxxxxxxx> > --- > drivers/net/wireless/ath/ath9k/ath9k.h | 3 +++ > drivers/net/wireless/ath/ath9k/debug.c | 1 + > drivers/net/wireless/ath/ath9k/debug.h | 1 + > drivers/net/wireless/ath/ath9k/init.c | 1 + > drivers/net/wireless/ath/ath9k/link.c | 31 ++++++++++++++++++++++++++ > drivers/net/wireless/ath/ath9k/main.c | 4 ++++ > 6 files changed, 41 insertions(+) > > diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h > index 29ca65a732a6..c1ce081445a9 100644 > --- a/drivers/net/wireless/ath/ath9k/ath9k.h > +++ b/drivers/net/wireless/ath/ath9k/ath9k.h > @@ -739,11 +739,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 4000 > > void ath_hw_check_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(struct timer_list *t); > void ath_start_ani(struct ath_softc *sc); > @@ -1044,6 +1046,7 @@ struct ath_softc { > #endif > struct delayed_work hw_check_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 eff894958a73..6b2469a01f17 100644 > --- a/drivers/net/wireless/ath/ath9k/debug.c > +++ b/drivers/net/wireless/ath/ath9k/debug.c > @@ -750,6 +750,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 389459c04d14..6ebb6053a8c1 100644 > --- a/drivers/net/wireless/ath/ath9k/debug.h > +++ b/drivers/net/wireless/ath/ath9k/debug.h > @@ -53,6 +53,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 f9e77c4624d9..833474d7281f 100644 > --- a/drivers/net/wireless/ath/ath9k/init.c > +++ b/drivers/net/wireless/ath/ath9k/init.c > @@ -740,6 +740,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, > INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); > INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); > INIT_DELAYED_WORK(&sc->hw_check_work, ath_hw_check_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 d1e5767aab3c..37438960c278 100644 > --- a/drivers/net/wireless/ath/ath9k/link.c > +++ b/drivers/net/wireless/ath/ath9k/link.c > @@ -142,6 +142,37 @@ 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; ath9k_hw_check_alive() already does this exact check... -Toke