ath9k: Add recovery mechanism for hw TSF timer
authorFelix Fietkau <nbd@openwrt.org>
Wed, 11 Jun 2014 10:48:14 +0000 (16:18 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 19 Jun 2014 19:49:20 +0000 (15:49 -0400)
Configure the TSF based hardware timer for a channel switch.
Also set up backup software timer, in case the gen timer fails.
This could be caused by a hardware reset.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/channel.c
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/main.c

index ee00ddb8885dd82f3394548aeeab5991ffe2c366..757dd602daaa4ddea1e06209fe9bcf235b5234b4 100644 (file)
@@ -374,6 +374,9 @@ struct ath_chanctx_sched {
        u32 switch_start_time;
        unsigned int offchannel_duration;
        unsigned int channel_switch_time;
+
+       /* backup, in case the hardware timer fails */
+       struct timer_list timer;
 };
 
 enum ath_offchannel_state {
@@ -426,6 +429,7 @@ struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc,
                                              bool active);
 void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
                       enum ath_chanctx_event ev);
+void ath_chanctx_timer(unsigned long data);
 
 int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
 int ath_startrecv(struct ath_softc *sc);
index 0a38eea278704a49defb157864e21d61694d72bc..ba214ebdcd167163cb38e50fb225b749d46005bf 100644 (file)
@@ -469,6 +469,27 @@ static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc)
        prev->tsf_val += offset;
 }
 
+void ath_chanctx_timer(unsigned long data)
+{
+       struct ath_softc *sc = (struct ath_softc *) data;
+
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
+}
+
+/* Configure the TSF based hardware timer for a channel switch.
+ * Also set up backup software timer, in case the gen timer fails.
+ * This could be caused by a hardware reset.
+ */
+static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time)
+{
+       struct ath_hw *ah = sc->sc_ah;
+
+       ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000);
+       tsf_time -= ath9k_hw_gettsf32(ah);
+       tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1;
+       mod_timer(&sc->sched.timer, tsf_time);
+}
+
 void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
                       enum ath_chanctx_event ev)
 {
@@ -566,9 +587,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
                        break;
 
                sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
-               ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer,
-                                        sc->sched.switch_start_time,
-                                        1000000);
+               ath_chanctx_setup_timer(sc, sc->sched.switch_start_time);
                break;
        case ATH_CHANCTX_EVENT_TSF_TIMER:
                if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
@@ -598,8 +617,8 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
                        ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL);
                tsf_time += ath9k_hw_gettsf32(ah);
 
-               ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer,
-                                        tsf_time, 1000000);
+
+               ath_chanctx_setup_timer(sc, tsf_time);
                break;
        case ATH_CHANCTX_EVENT_ASSOC:
                if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE ||
@@ -633,8 +652,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
                tsf_time += ath9k_hw_gettsf32(sc->sc_ah);
                sc->sched.switch_start_time = tsf_time;
 
-               ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer,
-                                        tsf_time, 1000000);
+               ath_chanctx_setup_timer(sc, tsf_time);
                sc->sched.beacon_pending = true;
                break;
        case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL:
index 66a9dc3a536931b919bf99746c858a15c2071339..a4afcb19af2a8a67eda054eb0e3d71411a3bac87 100644 (file)
@@ -569,6 +569,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
        setup_timer(&sc->offchannel.timer, ath_offchannel_timer,
                    (unsigned long)sc);
+       setup_timer(&sc->sched.timer, ath_chanctx_timer, (unsigned long)sc);
 
        /*
         * Cache line size is used to size and align various
index f7d8ddae216edadbfb82798fe929a2e1fa2a8851..b307e6e2c0be5e4f4e1d195a9a5b47ec317aca5e 100644 (file)
@@ -1668,6 +1668,7 @@ void ath9k_p2p_ps_timer(void *priv)
        struct ath_node *an;
        u32 tsf;
 
+       del_timer_sync(&sc->sched.timer);
        ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer);
        ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);