ath9k: Fix a PLL hang issue observed with AR9485.
authorVivek Natarajan <vnatarajan@atheros.com>
Thu, 27 Jan 2011 09:15:08 +0000 (14:45 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 28 Jan 2011 20:44:28 +0000 (15:44 -0500)
When this PLL hang issue is seen, both Rx and Tx fail to work.
The sqsum_dvc needs to be below 2000 for a good chip. During
this issue the sqsum_dvc value is beyond 80000 and only a
full reset can solve this problem.

Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/xmit.c

index 73db9576af57aca26df92bbcd2a2e63f0e840b7c..a23e9a8846932bea785647adcdc918fea91873ea 100644 (file)
@@ -641,6 +641,7 @@ struct ath_softc {
 #endif
        struct ath_beacon_config cur_beacon_conf;
        struct delayed_work tx_complete_work;
+       struct delayed_work hw_pll_work;
        struct ath_btcoex btcoex;
 
        struct ath_descdma txsdma;
index 23c016a81bcf0971b20c878e9fe6b37f705fb88b..0663a32d81d2a97c5dd8aa4a227d7571cec0476a 100644 (file)
@@ -230,6 +230,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
        cancel_work_sync(&sc->paprd_work);
        cancel_work_sync(&sc->hw_check_work);
        cancel_delayed_work_sync(&sc->tx_complete_work);
+       cancel_delayed_work_sync(&sc->hw_pll_work);
 
        ath9k_ps_wakeup(sc);
 
@@ -290,6 +291,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
                if (sc->sc_flags & SC_OP_BEACONS)
                        ath_beacon_config(sc, NULL);
                ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+               ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
                ath_start_ani(common);
        }
 
@@ -1263,6 +1265,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
                cancel_delayed_work_sync(&sc->ath_led_blink_work);
 
        cancel_delayed_work_sync(&sc->tx_complete_work);
+       cancel_delayed_work_sync(&sc->hw_pll_work);
        cancel_work_sync(&sc->paprd_work);
        cancel_work_sync(&sc->hw_check_work);
 
index 879365e9b1c9969b9a5248fde08b90135a5675cd..10a3dbefaa0923b1aa4014c06ed6e5ebb26b30d8 100644 (file)
@@ -2091,6 +2091,28 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
        }
 }
 
+static void ath_hw_pll_work(struct work_struct *work)
+{
+       struct ath_softc *sc = container_of(work, struct ath_softc,
+                                           hw_pll_work.work);
+       static int count;
+
+       if (AR_SREV_9485(sc->sc_ah)) {
+               if (ar9003_get_pll_sqsum_dvc(sc->sc_ah) >= 0x40000) {
+                       count++;
+
+                       if (count == 3) {
+                               /* Rx is hung for more than 500ms. Reset it */
+                               ath_reset(sc, true);
+                               count = 0;
+                       }
+               } else
+                       count = 0;
+
+               ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5);
+       }
+}
+
 static void ath_tx_complete_poll_work(struct work_struct *work)
 {
        struct ath_softc *sc = container_of(work, struct ath_softc,
@@ -2312,6 +2334,7 @@ int ath_tx_init(struct ath_softc *sc, int nbufs)
        }
 
        INIT_DELAYED_WORK(&sc->tx_complete_work, ath_tx_complete_poll_work);
+       INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
 
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
                error = ath_tx_edma_init(sc);