aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRajkumar Manoharan <rmanohar@qca.qualcomm.com>2012-03-14 20:04:27 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-04-09 16:05:53 -0400
commit01e189182d62d6ee3603233fc88f9235e9830b92 (patch)
tree6dee11b6c24d86b99747dc625bdeaed6a7da8f5e
parent54da20d83f0e7fe87b75aec44bc2b1448d119320 (diff)
ath9k: recover ar9380 chips from rare stuck state
In the experiment with Azimuth ADEPT-n testbed where the APs transmit power was reduced to 25% and the signal strength was futher attenuated by 20dB and induced a path loss of ~7dB, the station was reporting beacon losses and the following issue were observed. * rx clear is stuck at low for more than 300ms * dcu chain and complete state is stuck at one of the hang signature This patch triggers the hang detection logic that recovers the chip from any of the above conditions. As the issue was originally reported in ChromeOs with AR9382 chips, this detection logic is enabled only for AR9380/2 chips. Cc: Paul Stewart <pstew@google.com> Reported-by: Gary Morain <gmorain@google.com> Signed-off-by: Rajkumar Manoharan <rmanohar@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h3
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c73
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c48
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c4
6 files changed, 126 insertions, 4 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 8c84049682ab..0792d87558ef 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -430,6 +430,8 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status);
430void ath_reset_work(struct work_struct *work); 430void ath_reset_work(struct work_struct *work);
431void ath_hw_check(struct work_struct *work); 431void ath_hw_check(struct work_struct *work);
432void ath_hw_pll_work(struct work_struct *work); 432void ath_hw_pll_work(struct work_struct *work);
433void ath_rx_poll(unsigned long data);
434void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon);
433void ath_paprd_calibrate(struct work_struct *work); 435void ath_paprd_calibrate(struct work_struct *work);
434void ath_ani_calibrate(unsigned long data); 436void ath_ani_calibrate(unsigned long data);
435void ath_start_ani(struct ath_common *common); 437void ath_start_ani(struct ath_common *common);
@@ -670,6 +672,7 @@ struct ath_softc {
670 struct ath_beacon_config cur_beacon_conf; 672 struct ath_beacon_config cur_beacon_conf;
671 struct delayed_work tx_complete_work; 673 struct delayed_work tx_complete_work;
672 struct delayed_work hw_pll_work; 674 struct delayed_work hw_pll_work;
675 struct timer_list rx_poll_timer;
673 676
674#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT 677#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
675 struct ath_btcoex btcoex; 678 struct ath_btcoex btcoex;
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 64fcfad467bf..2d47f747512e 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -174,6 +174,7 @@ enum ath_reset_type {
174 RESET_TYPE_TX_ERROR, 174 RESET_TYPE_TX_ERROR,
175 RESET_TYPE_TX_HANG, 175 RESET_TYPE_TX_HANG,
176 RESET_TYPE_PLL_HANG, 176 RESET_TYPE_PLL_HANG,
177 RESET_TYPE_MAC_HANG,
177 __RESET_TYPE_MAX 178 __RESET_TYPE_MAX
178}; 179};
179 180
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 6c69e4e8b1cb..d1345a8a2b15 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1491,11 +1491,84 @@ static void ath9k_hw_apply_gpio_override(struct ath_hw *ah)
1491 } 1491 }
1492} 1492}
1493 1493
1494static bool ath9k_hw_check_dcs(u32 dma_dbg, u32 num_dcu_states,
1495 int *hang_state, int *hang_pos)
1496{
1497 static u32 dcu_chain_state[] = {5, 6, 9}; /* DCU chain stuck states */
1498 u32 chain_state, dcs_pos, i;
1499
1500 for (dcs_pos = 0; dcs_pos < num_dcu_states; dcs_pos++) {
1501 chain_state = (dma_dbg >> (5 * dcs_pos)) & 0x1f;
1502 for (i = 0; i < 3; i++) {
1503 if (chain_state == dcu_chain_state[i]) {
1504 *hang_state = chain_state;
1505 *hang_pos = dcs_pos;
1506 return true;
1507 }
1508 }
1509 }
1510 return false;
1511}
1512
1513#define DCU_COMPLETE_STATE 1
1514#define DCU_COMPLETE_STATE_MASK 0x3
1515#define NUM_STATUS_READS 50
1516static bool ath9k_hw_detect_mac_hang(struct ath_hw *ah)
1517{
1518 u32 chain_state, comp_state, dcs_reg = AR_DMADBG_4;
1519 u32 i, hang_pos, hang_state, num_state = 6;
1520
1521 comp_state = REG_READ(ah, AR_DMADBG_6);
1522
1523 if ((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) {
1524 ath_dbg(ath9k_hw_common(ah), RESET,
1525 "MAC Hang signature not found at DCU complete\n");
1526 return false;
1527 }
1528
1529 chain_state = REG_READ(ah, dcs_reg);
1530 if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
1531 goto hang_check_iter;
1532
1533 dcs_reg = AR_DMADBG_5;
1534 num_state = 4;
1535 chain_state = REG_READ(ah, dcs_reg);
1536 if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
1537 goto hang_check_iter;
1538
1539 ath_dbg(ath9k_hw_common(ah), RESET,
1540 "MAC Hang signature 1 not found\n");
1541 return false;
1542
1543hang_check_iter:
1544 ath_dbg(ath9k_hw_common(ah), RESET,
1545 "DCU registers: chain %08x complete %08x Hang: state %d pos %d\n",
1546 chain_state, comp_state, hang_state, hang_pos);
1547
1548 for (i = 0; i < NUM_STATUS_READS; i++) {
1549 chain_state = REG_READ(ah, dcs_reg);
1550 chain_state = (chain_state >> (5 * hang_pos)) & 0x1f;
1551 comp_state = REG_READ(ah, AR_DMADBG_6);
1552
1553 if (((comp_state & DCU_COMPLETE_STATE_MASK) !=
1554 DCU_COMPLETE_STATE) ||
1555 (chain_state != hang_state))
1556 return false;
1557 }
1558
1559 ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature 1 found\n");
1560
1561 return true;
1562}
1563
1494bool ath9k_hw_check_alive(struct ath_hw *ah) 1564bool ath9k_hw_check_alive(struct ath_hw *ah)
1495{ 1565{
1496 int count = 50; 1566 int count = 50;
1497 u32 reg; 1567 u32 reg;
1498 1568
1569 if (AR_SREV_9300(ah))
1570 return !ath9k_hw_detect_mac_hang(ah);
1571
1499 if (AR_SREV_9285_12_OR_LATER(ah)) 1572 if (AR_SREV_9285_12_OR_LATER(ah))
1500 return true; 1573 return true;
1501 1574
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index cb006458fc4b..b8f3423fdbf9 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -779,6 +779,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
779 goto error_world; 779 goto error_world;
780 } 780 }
781 781
782 setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc);
782 sc->last_rssi = ATH_RSSI_DUMMY_MARKER; 783 sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
783 784
784 ath_init_leds(sc); 785 ath_init_leds(sc);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 38794850f005..eeea81b16d9c 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -243,6 +243,7 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
243 243
244 sc->hw_busy_count = 0; 244 sc->hw_busy_count = 0;
245 del_timer_sync(&common->ani.timer); 245 del_timer_sync(&common->ani.timer);
246 del_timer_sync(&sc->rx_poll_timer);
246 247
247 ath9k_debug_samp_bb_mac(sc); 248 ath9k_debug_samp_bb_mac(sc);
248 ath9k_hw_disable_interrupts(ah); 249 ath9k_hw_disable_interrupts(ah);
@@ -284,6 +285,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
284 285
285 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); 286 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
286 ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2); 287 ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
288 ath_start_rx_poll(sc, 3);
287 if (!common->disable_ani) 289 if (!common->disable_ani)
288 ath_start_ani(common); 290 ath_start_ani(common);
289 } 291 }
@@ -914,10 +916,19 @@ void ath_hw_check(struct work_struct *work)
914 struct ath_common *common = ath9k_hw_common(sc->sc_ah); 916 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
915 unsigned long flags; 917 unsigned long flags;
916 int busy; 918 int busy;
919 u8 is_alive, nbeacon = 1;
917 920
918 ath9k_ps_wakeup(sc); 921 ath9k_ps_wakeup(sc);
919 if (ath9k_hw_check_alive(sc->sc_ah)) 922 is_alive = ath9k_hw_check_alive(sc->sc_ah);
923
924 if (is_alive && !AR_SREV_9300(sc->sc_ah))
920 goto out; 925 goto out;
926 else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
927 ath_dbg(common, RESET,
928 "DCU stuck is detected. Schedule chip reset\n");
929 RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG);
930 goto sched_reset;
931 }
921 932
922 spin_lock_irqsave(&common->cc_lock, flags); 933 spin_lock_irqsave(&common->cc_lock, flags);
923 busy = ath_update_survey_stats(sc); 934 busy = ath_update_survey_stats(sc);
@@ -928,12 +939,18 @@ void ath_hw_check(struct work_struct *work)
928 if (busy >= 99) { 939 if (busy >= 99) {
929 if (++sc->hw_busy_count >= 3) { 940 if (++sc->hw_busy_count >= 3) {
930 RESET_STAT_INC(sc, RESET_TYPE_BB_HANG); 941 RESET_STAT_INC(sc, RESET_TYPE_BB_HANG);
931 ieee80211_queue_work(sc->hw, &sc->hw_reset_work); 942 goto sched_reset;
932 } 943 }
933 944 } else if (busy >= 0) {
934 } else if (busy >= 0)
935 sc->hw_busy_count = 0; 945 sc->hw_busy_count = 0;
946 nbeacon = 3;
947 }
936 948
949 ath_start_rx_poll(sc, nbeacon);
950 goto out;
951
952sched_reset:
953 ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
937out: 954out:
938 ath9k_ps_restore(sc); 955 ath9k_ps_restore(sc);
939} 956}
@@ -1153,6 +1170,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
1153 mutex_lock(&sc->mutex); 1170 mutex_lock(&sc->mutex);
1154 1171
1155 ath_cancel_work(sc); 1172 ath_cancel_work(sc);
1173 del_timer_sync(&sc->rx_poll_timer);
1156 1174
1157 if (sc->sc_flags & SC_OP_INVALID) { 1175 if (sc->sc_flags & SC_OP_INVALID) {
1158 ath_dbg(common, ANY, "Device not present\n"); 1176 ath_dbg(common, ANY, "Device not present\n");
@@ -1385,6 +1403,24 @@ static void ath9k_do_vif_add_setup(struct ieee80211_hw *hw,
1385 } 1403 }
1386} 1404}
1387 1405
1406void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon)
1407{
1408 if (!AR_SREV_9300(sc->sc_ah))
1409 return;
1410
1411 if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF))
1412 return;
1413
1414 mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies
1415 (nbeacon * sc->cur_beacon_conf.beacon_interval));
1416}
1417
1418void ath_rx_poll(unsigned long data)
1419{
1420 struct ath_softc *sc = (struct ath_softc *)data;
1421
1422 ieee80211_queue_work(sc->hw, &sc->hw_check_work);
1423}
1388 1424
1389static int ath9k_add_interface(struct ieee80211_hw *hw, 1425static int ath9k_add_interface(struct ieee80211_hw *hw,
1390 struct ieee80211_vif *vif) 1426 struct ieee80211_vif *vif)
@@ -1906,6 +1942,8 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
1906 sc->last_rssi = ATH_RSSI_DUMMY_MARKER; 1942 sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
1907 sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; 1943 sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
1908 1944
1945 ath_start_rx_poll(sc, 3);
1946
1909 if (!common->disable_ani) { 1947 if (!common->disable_ani) {
1910 sc->sc_flags |= SC_OP_ANI_RUN; 1948 sc->sc_flags |= SC_OP_ANI_RUN;
1911 ath_start_ani(common); 1949 ath_start_ani(common);
@@ -1945,6 +1983,7 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif)
1945 /* Stop ANI */ 1983 /* Stop ANI */
1946 sc->sc_flags &= ~SC_OP_ANI_RUN; 1984 sc->sc_flags &= ~SC_OP_ANI_RUN;
1947 del_timer_sync(&common->ani.timer); 1985 del_timer_sync(&common->ani.timer);
1986 del_timer_sync(&sc->rx_poll_timer);
1948 memset(&sc->caldata, 0, sizeof(sc->caldata)); 1987 memset(&sc->caldata, 0, sizeof(sc->caldata));
1949 } 1988 }
1950} 1989}
@@ -1988,6 +2027,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
1988 } else { 2027 } else {
1989 sc->sc_flags &= ~SC_OP_ANI_RUN; 2028 sc->sc_flags &= ~SC_OP_ANI_RUN;
1990 del_timer_sync(&common->ani.timer); 2029 del_timer_sync(&common->ani.timer);
2030 del_timer_sync(&sc->rx_poll_timer);
1991 } 2031 }
1992 } 2032 }
1993 2033
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 1c4583c7ff7c..858801735282 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -1855,6 +1855,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
1855 if (retval) 1855 if (retval)
1856 goto requeue_drop_frag; 1856 goto requeue_drop_frag;
1857 1857
1858 if (rs.is_mybeacon) {
1859 sc->hw_busy_count = 0;
1860 ath_start_rx_poll(sc, 3);
1861 }
1858 /* Ensure we always have an skb to requeue once we are done 1862 /* Ensure we always have an skb to requeue once we are done
1859 * processing the current buffer's skb */ 1863 * processing the current buffer's skb */
1860 requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC); 1864 requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC);