5314883fcff5a9a10fc527fca4503628f737cdb4
[openwrt-10.03/.git] / package / mac80211 / patches / 570-ath9k_bb_hang_check_fix.patch
1 --- a/drivers/net/wireless/ath/ath9k/main.c
2 +++ b/drivers/net/wireless/ath/ath9k/main.c
3 @@ -177,7 +177,12 @@ static void ath_update_survey_nf(struct 
4         }
5  }
6  
7 -static void ath_update_survey_stats(struct ath_softc *sc)
8 +/*
9 + * Updates the survey statistics and returns the busy time since last
10 + * update in %, if the measurement duration was long enough for the
11 + * result to be useful, -1 otherwise.
12 + */
13 +static int ath_update_survey_stats(struct ath_softc *sc)
14  {
15         struct ath_hw *ah = sc->sc_ah;
16         struct ath_common *common = ath9k_hw_common(ah);
17 @@ -185,9 +190,10 @@ static void ath_update_survey_stats(stru
18         struct survey_info *survey = &sc->survey[pos];
19         struct ath_cycle_counters *cc = &common->cc_survey;
20         unsigned int div = common->clockrate * 1000;
21 +       int ret = 0;
22  
23         if (!ah->curchan)
24 -               return;
25 +               return -1;
26  
27         if (ah->power_mode == ATH9K_PM_AWAKE)
28                 ath_hw_cycle_counters_update(common);
29 @@ -202,9 +208,18 @@ static void ath_update_survey_stats(stru
30                 survey->channel_time_rx += cc->rx_frame / div;
31                 survey->channel_time_tx += cc->tx_frame / div;
32         }
33 +
34 +       if (cc->cycles < div)
35 +               return -1;
36 +
37 +       if (cc->cycles > 0)
38 +               ret = cc->rx_busy * 100 / cc->cycles;
39 +
40         memset(cc, 0, sizeof(*cc));
41  
42         ath_update_survey_nf(sc, pos);
43 +
44 +       return ret;
45  }
46  
47  /*
48 @@ -226,6 +241,8 @@ int ath_set_channel(struct ath_softc *sc
49         if (sc->sc_flags & SC_OP_INVALID)
50                 return -EIO;
51  
52 +       sc->hw_busy_count = 0;
53 +
54         del_timer_sync(&common->ani.timer);
55         cancel_work_sync(&sc->paprd_work);
56         cancel_work_sync(&sc->hw_check_work);
57 @@ -584,17 +601,25 @@ static void ath_node_detach(struct ath_s
58  void ath_hw_check(struct work_struct *work)
59  {
60         struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
61 -       int i;
62 +       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
63 +       unsigned long flags;
64 +       int busy;
65  
66         ath9k_ps_wakeup(sc);
67 +       if (ath9k_hw_check_alive(sc->sc_ah))
68 +               goto out;
69  
70 -       for (i = 0; i < 3; i++) {
71 -               if (ath9k_hw_check_alive(sc->sc_ah))
72 -                       goto out;
73 +       spin_lock_irqsave(&common->cc_lock, flags);
74 +       busy = ath_update_survey_stats(sc);
75 +       spin_unlock_irqrestore(&common->cc_lock, flags);
76  
77 -               msleep(1);
78 -       }
79 -       ath_reset(sc, true);
80 +       ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, "
81 +               "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1);
82 +       if (busy >= 99) {
83 +               if (++sc->hw_busy_count >= 3)
84 +                       ath_reset(sc, true);
85 +       } else if (busy >= 0)
86 +               sc->hw_busy_count = 0;
87  
88  out:
89         ath9k_ps_restore(sc);
90 @@ -988,6 +1013,8 @@ int ath_reset(struct ath_softc *sc, bool
91         struct ieee80211_hw *hw = sc->hw;
92         int r;
93  
94 +       sc->hw_busy_count = 0;
95 +
96         /* Stop ANI */
97         del_timer_sync(&common->ani.timer);
98  
99 --- a/drivers/net/wireless/ath/ath9k/ath9k.h
100 +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
101 @@ -598,6 +598,8 @@ struct ath_softc {
102         struct completion paprd_complete;
103         bool paprd_pending;
104  
105 +       unsigned int hw_busy_count;
106 +
107         u32 intrstatus;
108         u32 sc_flags; /* SC_OP_* */
109         u16 ps_flags; /* PS_* */