aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com>2012-08-29 10:10:27 -0400
committerKalle Valo <kvalo@qca.qualcomm.com>2012-10-24 04:49:48 -0400
commit9233299394de1c571e52ab2dbe1995c1fbdc8fda (patch)
tree875d999ad74083140f1560215a88bc94a6e37286
parent84caf8005b09e0a4a57fce44119489d1b0bbbe94 (diff)
ath6kl: Add support to detect fw error through heart beat
This patch adds support to detect fw error condition by sending periodic message (heart beat challenge) to firmware. Upon reception of the message, fw would send a response event to driver. When there are no reponses from fw for about 5 cmd driver would trigger the recovery logic assuming that fw has gone into an error state. Capable fw will advertise this capability through ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL bit. This feature is disabled by default, can be enabled through a modparam (heart_beat_poll). This modparam also confiures the polling interval in msecs. Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.c9
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.h16
-rw-r--r--drivers/net/wireless/ath/ath6kl/init.c6
-rw-r--r--drivers/net/wireless/ath/ath6kl/recovery.c89
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.c14
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.h2
7 files changed, 129 insertions, 9 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 8cf146b5c57..c8b6be44fa2 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -2517,7 +2517,7 @@ static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
2517 if (err) 2517 if (err)
2518 return err; 2518 return err;
2519 2519
2520 ar->fw_recovery.enable = true; 2520 ath6kl_recovery_resume(ar);
2521 2521
2522 return 0; 2522 return 0;
2523} 2523}
diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c
index adcaa965486..fd5dd3aca77 100644
--- a/drivers/net/wireless/ath/ath6kl/core.c
+++ b/drivers/net/wireless/ath/ath6kl/core.c
@@ -33,6 +33,7 @@ static unsigned int wow_mode;
33static unsigned int uart_debug; 33static unsigned int uart_debug;
34static unsigned int ath6kl_p2p; 34static unsigned int ath6kl_p2p;
35static unsigned int testmode; 35static unsigned int testmode;
36static unsigned int heart_beat_poll;
36 37
37module_param(debug_mask, uint, 0644); 38module_param(debug_mask, uint, 0644);
38module_param(suspend_mode, uint, 0644); 39module_param(suspend_mode, uint, 0644);
@@ -40,6 +41,9 @@ module_param(wow_mode, uint, 0644);
40module_param(uart_debug, uint, 0644); 41module_param(uart_debug, uint, 0644);
41module_param(ath6kl_p2p, uint, 0644); 42module_param(ath6kl_p2p, uint, 0644);
42module_param(testmode, uint, 0644); 43module_param(testmode, uint, 0644);
44module_param(heart_beat_poll, uint, 0644);
45MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic" \
46 "polling. This also specifies the polling interval in msecs");
43 47
44void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) 48void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb)
45{ 49{
@@ -202,6 +206,11 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type)
202 ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", 206 ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n",
203 __func__, wdev->netdev->name, wdev->netdev, ar); 207 __func__, wdev->netdev->name, wdev->netdev, ar);
204 208
209 if (heart_beat_poll &&
210 test_bit(ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL,
211 ar->fw_capabilities))
212 ar->fw_recovery.hb_poll = heart_beat_poll;
213
205 ath6kl_recovery_init(ar); 214 ath6kl_recovery_init(ar);
206 215
207 return ret; 216 return ret;
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index c7dcdadd8b8..b2cbecf6cfe 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -130,6 +130,12 @@ enum ath6kl_fw_capability {
130 /* Firmware supports sched scan decoupled from host sleep */ 130 /* Firmware supports sched scan decoupled from host sleep */
131 ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, 131 ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2,
132 132
133 /*
134 * Firmware capability for hang detection through heart beat
135 * challenge messages.
136 */
137 ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL,
138
133 /* this needs to be last */ 139 /* this needs to be last */
134 ATH6KL_FW_CAPABILITY_MAX, 140 ATH6KL_FW_CAPABILITY_MAX,
135}; 141};
@@ -649,8 +655,11 @@ enum ath6kl_state {
649}; 655};
650 656
651/* Fw error recovery */ 657/* Fw error recovery */
658#define ATH6KL_HB_RESP_MISS_THRES 5
659
652enum ath6kl_fw_err { 660enum ath6kl_fw_err {
653 ATH6KL_FW_ASSERT, 661 ATH6KL_FW_ASSERT,
662 ATH6KL_FW_HB_RESP_FAILURE,
654}; 663};
655 664
656struct ath6kl { 665struct ath6kl {
@@ -800,6 +809,11 @@ struct ath6kl {
800 bool enable; 809 bool enable;
801 struct work_struct recovery_work; 810 struct work_struct recovery_work;
802 unsigned long err_reason; 811 unsigned long err_reason;
812 unsigned long hb_poll;
813 struct timer_list hb_timer;
814 u32 seq_num;
815 bool hb_pending;
816 u8 hb_misscnt;
803 } fw_recovery; 817 } fw_recovery;
804 818
805#ifdef CONFIG_ATH6KL_DEBUG 819#ifdef CONFIG_ATH6KL_DEBUG
@@ -940,7 +954,9 @@ void ath6kl_core_destroy(struct ath6kl *ar);
940/* Fw error recovery */ 954/* Fw error recovery */
941void ath6kl_init_hw_restart(struct ath6kl *ar); 955void ath6kl_init_hw_restart(struct ath6kl *ar);
942void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason); 956void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason);
957void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie);
943void ath6kl_recovery_init(struct ath6kl *ar); 958void ath6kl_recovery_init(struct ath6kl *ar);
944void ath6kl_recovery_cleanup(struct ath6kl *ar); 959void ath6kl_recovery_cleanup(struct ath6kl *ar);
945void ath6kl_recovery_suspend(struct ath6kl *ar); 960void ath6kl_recovery_suspend(struct ath6kl *ar);
961void ath6kl_recovery_resume(struct ath6kl *ar);
946#endif /* CORE_H */ 962#endif /* CORE_H */
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index 301443c9f9e..6e270fa6d63 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -1697,9 +1697,6 @@ int ath6kl_init_hw_stop(struct ath6kl *ar)
1697 1697
1698void ath6kl_init_hw_restart(struct ath6kl *ar) 1698void ath6kl_init_hw_restart(struct ath6kl *ar)
1699{ 1699{
1700
1701 ar->state = ATH6KL_STATE_RECOVERY;
1702
1703 ath6kl_cfg80211_stop_all(ar); 1700 ath6kl_cfg80211_stop_all(ar);
1704 1701
1705 if (__ath6kl_init_hw_stop(ar)) 1702 if (__ath6kl_init_hw_stop(ar))
@@ -1709,9 +1706,6 @@ void ath6kl_init_hw_restart(struct ath6kl *ar)
1709 ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n"); 1706 ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n");
1710 return; 1707 return;
1711 } 1708 }
1712
1713 ar->state = ATH6KL_STATE_ON;
1714 ar->fw_recovery.err_reason = 0;
1715} 1709}
1716 1710
1717/* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */ 1711/* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */
diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c
index c225fc4d3d5..4e3f205bb8a 100644
--- a/drivers/net/wireless/ath/ath6kl/recovery.c
+++ b/drivers/net/wireless/ath/ath6kl/recovery.c
@@ -23,7 +23,18 @@ static void ath6kl_recovery_work(struct work_struct *work)
23 struct ath6kl *ar = container_of(work, struct ath6kl, 23 struct ath6kl *ar = container_of(work, struct ath6kl,
24 fw_recovery.recovery_work); 24 fw_recovery.recovery_work);
25 25
26 ar->state = ATH6KL_STATE_RECOVERY;
27
28 del_timer_sync(&ar->fw_recovery.hb_timer);
29
26 ath6kl_init_hw_restart(ar); 30 ath6kl_init_hw_restart(ar);
31
32 ar->state = ATH6KL_STATE_ON;
33 ar->fw_recovery.err_reason = 0;
34
35 if (ar->fw_recovery.enable)
36 mod_timer(&ar->fw_recovery.hb_timer, jiffies +
37 msecs_to_jiffies(ar->fw_recovery.hb_poll));
27} 38}
28 39
29void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) 40void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason)
@@ -37,18 +48,72 @@ void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason)
37 queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work); 48 queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work);
38} 49}
39 50
51void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie)
52{
53 if (cookie == ar->fw_recovery.seq_num)
54 ar->fw_recovery.hb_pending = false;
55}
56
57static void ath6kl_recovery_hb_timer(unsigned long data)
58{
59 struct ath6kl *ar = (struct ath6kl *) data;
60 int err;
61
62 if (!ar->fw_recovery.enable)
63 return;
64
65 if (ar->fw_recovery.hb_pending)
66 ar->fw_recovery.hb_misscnt++;
67 else
68 ar->fw_recovery.hb_misscnt = 0;
69
70 if (ar->fw_recovery.hb_misscnt > ATH6KL_HB_RESP_MISS_THRES) {
71 ar->fw_recovery.hb_misscnt = 0;
72 ar->fw_recovery.seq_num = 0;
73 ar->fw_recovery.hb_pending = false;
74 ath6kl_recovery_err_notify(ar, ATH6KL_FW_HB_RESP_FAILURE);
75 return;
76 }
77
78 ar->fw_recovery.seq_num++;
79 ar->fw_recovery.hb_pending = true;
80
81 err = ath6kl_wmi_get_challenge_resp_cmd(ar->wmi,
82 ar->fw_recovery.seq_num, 0);
83 if (err)
84 ath6kl_warn("Failed to send hb challenge request, err:%d\n",
85 err);
86
87 if ((ar->state == ATH6KL_STATE_RECOVERY) || !ar->fw_recovery.enable)
88 return;
89
90 mod_timer(&ar->fw_recovery.hb_timer, jiffies +
91 msecs_to_jiffies(ar->fw_recovery.hb_poll));
92}
93
40void ath6kl_recovery_init(struct ath6kl *ar) 94void ath6kl_recovery_init(struct ath6kl *ar)
41{ 95{
42 struct ath6kl_fw_recovery *recovery = &ar->fw_recovery; 96 struct ath6kl_fw_recovery *recovery = &ar->fw_recovery;
43 97
44 recovery->enable = true; 98 recovery->enable = true;
45 INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work); 99 INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work);
100 recovery->seq_num = 0;
101 recovery->hb_misscnt = 0;
102 ar->fw_recovery.hb_pending = false;
103 ar->fw_recovery.hb_timer.function = ath6kl_recovery_hb_timer;
104 ar->fw_recovery.hb_timer.data = (unsigned long) ar;
105 init_timer_deferrable(&ar->fw_recovery.hb_timer);
106
107 if (ar->fw_recovery.hb_poll)
108 mod_timer(&ar->fw_recovery.hb_timer, jiffies +
109 msecs_to_jiffies(ar->fw_recovery.hb_poll));
46} 110}
47 111
48void ath6kl_recovery_cleanup(struct ath6kl *ar) 112void ath6kl_recovery_cleanup(struct ath6kl *ar)
49{ 113{
50 ar->fw_recovery.enable = false; 114 ar->fw_recovery.enable = false;
51 115
116 del_timer_sync(&ar->fw_recovery.hb_timer);
52 cancel_work_sync(&ar->fw_recovery.recovery_work); 117 cancel_work_sync(&ar->fw_recovery.recovery_work);
53} 118}
54 119
@@ -56,7 +121,27 @@ void ath6kl_recovery_suspend(struct ath6kl *ar)
56{ 121{
57 ath6kl_recovery_cleanup(ar); 122 ath6kl_recovery_cleanup(ar);
58 123
124 if (!ar->fw_recovery.err_reason)
125 return;
126
59 /* Process pending fw error detection */ 127 /* Process pending fw error detection */
60 if (ar->fw_recovery.err_reason) 128 ar->fw_recovery.err_reason = 0;
61 ath6kl_init_hw_restart(ar); 129 WARN_ON(ar->state != ATH6KL_STATE_ON);
130 ar->state = ATH6KL_STATE_RECOVERY;
131 ath6kl_init_hw_restart(ar);
132 ar->state = ATH6KL_STATE_ON;
133}
134
135void ath6kl_recovery_resume(struct ath6kl *ar)
136{
137 ar->fw_recovery.enable = true;
138
139 if (!ar->fw_recovery.hb_poll)
140 return;
141
142 ar->fw_recovery.hb_pending = false;
143 ar->fw_recovery.seq_num = 0;
144 ar->fw_recovery.hb_misscnt = 0;
145 mod_timer(&ar->fw_recovery.hb_timer,
146 jiffies + msecs_to_jiffies(ar->fw_recovery.hb_poll));
62} 147}
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index cd2db42c098..68b46bda498 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -3767,6 +3767,19 @@ int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout)
3767 NO_SYNC_WMIFLAG); 3767 NO_SYNC_WMIFLAG);
3768} 3768}
3769 3769
3770static void ath6kl_wmi_hb_challenge_resp_event(struct wmi *wmi, u8 *datap,
3771 int len)
3772{
3773 struct wmix_hb_challenge_resp_cmd *cmd;
3774
3775 if (len < sizeof(struct wmix_hb_challenge_resp_cmd))
3776 return;
3777
3778 cmd = (struct wmix_hb_challenge_resp_cmd *) datap;
3779 ath6kl_recovery_hb_event(wmi->parent_dev,
3780 le32_to_cpu(cmd->cookie));
3781}
3782
3770static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) 3783static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
3771{ 3784{
3772 struct wmix_cmd_hdr *cmd; 3785 struct wmix_cmd_hdr *cmd;
@@ -3791,6 +3804,7 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
3791 switch (id) { 3804 switch (id) {
3792 case WMIX_HB_CHALLENGE_RESP_EVENTID: 3805 case WMIX_HB_CHALLENGE_RESP_EVENTID:
3793 ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n"); 3806 ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n");
3807 ath6kl_wmi_hb_challenge_resp_event(wmi, datap, len);
3794 break; 3808 break;
3795 case WMIX_DBGLOG_EVENTID: 3809 case WMIX_DBGLOG_EVENTID:
3796 ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len); 3810 ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len);
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h
index e916e57c9d9..98b1755e67f 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.h
+++ b/drivers/net/wireless/ath/ath6kl/wmi.h
@@ -2716,6 +2716,8 @@ int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout);
2716 2716
2717void ath6kl_wmi_sscan_timer(unsigned long ptr); 2717void ath6kl_wmi_sscan_timer(unsigned long ptr);
2718 2718
2719int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source);
2720
2719struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx); 2721struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx);
2720void *ath6kl_wmi_init(struct ath6kl *devt); 2722void *ath6kl_wmi_init(struct ath6kl *devt);
2721void ath6kl_wmi_shutdown(struct wmi *wmi); 2723void ath6kl_wmi_shutdown(struct wmi *wmi);