18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2012 Qualcomm Atheros, Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "core.h"
188c2ecf20Sopenharmony_ci#include "cfg80211.h"
198c2ecf20Sopenharmony_ci#include "debug.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic void ath6kl_recovery_work(struct work_struct *work)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct ath6kl *ar = container_of(work, struct ath6kl,
248c2ecf20Sopenharmony_ci					 fw_recovery.recovery_work);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	ar->state = ATH6KL_STATE_RECOVERY;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	del_timer_sync(&ar->fw_recovery.hb_timer);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	ath6kl_init_hw_restart(ar);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	ar->state = ATH6KL_STATE_ON;
338c2ecf20Sopenharmony_ci	clear_bit(WMI_CTRL_EP_FULL, &ar->flag);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	ar->fw_recovery.err_reason = 0;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (ar->fw_recovery.hb_poll)
388c2ecf20Sopenharmony_ci		mod_timer(&ar->fw_recovery.hb_timer, jiffies +
398c2ecf20Sopenharmony_ci			  msecs_to_jiffies(ar->fw_recovery.hb_poll));
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_civoid ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	if (!ar->fw_recovery.enable)
458c2ecf20Sopenharmony_ci		return;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Fw error detected, reason:%d\n",
488c2ecf20Sopenharmony_ci		   reason);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	set_bit(reason, &ar->fw_recovery.err_reason);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (!test_bit(RECOVERY_CLEANUP, &ar->flag) &&
538c2ecf20Sopenharmony_ci	    ar->state != ATH6KL_STATE_RECOVERY)
548c2ecf20Sopenharmony_ci		queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_civoid ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	if (cookie == ar->fw_recovery.seq_num)
608c2ecf20Sopenharmony_ci		ar->fw_recovery.hb_pending = false;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void ath6kl_recovery_hb_timer(struct timer_list *t)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct ath6kl *ar = from_timer(ar, t, fw_recovery.hb_timer);
668c2ecf20Sopenharmony_ci	int err;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (test_bit(RECOVERY_CLEANUP, &ar->flag) ||
698c2ecf20Sopenharmony_ci	    (ar->state == ATH6KL_STATE_RECOVERY))
708c2ecf20Sopenharmony_ci		return;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (ar->fw_recovery.hb_pending)
738c2ecf20Sopenharmony_ci		ar->fw_recovery.hb_misscnt++;
748c2ecf20Sopenharmony_ci	else
758c2ecf20Sopenharmony_ci		ar->fw_recovery.hb_misscnt = 0;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (ar->fw_recovery.hb_misscnt > ATH6KL_HB_RESP_MISS_THRES) {
788c2ecf20Sopenharmony_ci		ar->fw_recovery.hb_misscnt = 0;
798c2ecf20Sopenharmony_ci		ar->fw_recovery.seq_num = 0;
808c2ecf20Sopenharmony_ci		ar->fw_recovery.hb_pending = false;
818c2ecf20Sopenharmony_ci		ath6kl_recovery_err_notify(ar, ATH6KL_FW_HB_RESP_FAILURE);
828c2ecf20Sopenharmony_ci		return;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	ar->fw_recovery.seq_num++;
868c2ecf20Sopenharmony_ci	ar->fw_recovery.hb_pending = true;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	err = ath6kl_wmi_get_challenge_resp_cmd(ar->wmi,
898c2ecf20Sopenharmony_ci						ar->fw_recovery.seq_num, 0);
908c2ecf20Sopenharmony_ci	if (err)
918c2ecf20Sopenharmony_ci		ath6kl_warn("Failed to send hb challenge request, err:%d\n",
928c2ecf20Sopenharmony_ci			    err);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	mod_timer(&ar->fw_recovery.hb_timer, jiffies +
958c2ecf20Sopenharmony_ci		  msecs_to_jiffies(ar->fw_recovery.hb_poll));
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_civoid ath6kl_recovery_init(struct ath6kl *ar)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct ath6kl_fw_recovery *recovery = &ar->fw_recovery;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	clear_bit(RECOVERY_CLEANUP, &ar->flag);
1038c2ecf20Sopenharmony_ci	INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work);
1048c2ecf20Sopenharmony_ci	recovery->seq_num = 0;
1058c2ecf20Sopenharmony_ci	recovery->hb_misscnt = 0;
1068c2ecf20Sopenharmony_ci	ar->fw_recovery.hb_pending = false;
1078c2ecf20Sopenharmony_ci	timer_setup(&ar->fw_recovery.hb_timer, ath6kl_recovery_hb_timer,
1088c2ecf20Sopenharmony_ci		    TIMER_DEFERRABLE);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (ar->fw_recovery.hb_poll)
1118c2ecf20Sopenharmony_ci		mod_timer(&ar->fw_recovery.hb_timer, jiffies +
1128c2ecf20Sopenharmony_ci			  msecs_to_jiffies(ar->fw_recovery.hb_poll));
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_civoid ath6kl_recovery_cleanup(struct ath6kl *ar)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	if (!ar->fw_recovery.enable)
1188c2ecf20Sopenharmony_ci		return;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	set_bit(RECOVERY_CLEANUP, &ar->flag);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	del_timer_sync(&ar->fw_recovery.hb_timer);
1238c2ecf20Sopenharmony_ci	cancel_work_sync(&ar->fw_recovery.recovery_work);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_civoid ath6kl_recovery_suspend(struct ath6kl *ar)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	if (!ar->fw_recovery.enable)
1298c2ecf20Sopenharmony_ci		return;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	ath6kl_recovery_cleanup(ar);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (!ar->fw_recovery.err_reason)
1348c2ecf20Sopenharmony_ci		return;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* Process pending fw error detection */
1378c2ecf20Sopenharmony_ci	ar->fw_recovery.err_reason = 0;
1388c2ecf20Sopenharmony_ci	WARN_ON(ar->state != ATH6KL_STATE_ON);
1398c2ecf20Sopenharmony_ci	ar->state = ATH6KL_STATE_RECOVERY;
1408c2ecf20Sopenharmony_ci	ath6kl_init_hw_restart(ar);
1418c2ecf20Sopenharmony_ci	ar->state = ATH6KL_STATE_ON;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_civoid ath6kl_recovery_resume(struct ath6kl *ar)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	if (!ar->fw_recovery.enable)
1478c2ecf20Sopenharmony_ci		return;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	clear_bit(RECOVERY_CLEANUP, &ar->flag);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (!ar->fw_recovery.hb_poll)
1528c2ecf20Sopenharmony_ci		return;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	ar->fw_recovery.hb_pending = false;
1558c2ecf20Sopenharmony_ci	ar->fw_recovery.seq_num = 0;
1568c2ecf20Sopenharmony_ci	ar->fw_recovery.hb_misscnt = 0;
1578c2ecf20Sopenharmony_ci	mod_timer(&ar->fw_recovery.hb_timer,
1588c2ecf20Sopenharmony_ci		  jiffies + msecs_to_jiffies(ar->fw_recovery.hb_poll));
1598c2ecf20Sopenharmony_ci}
160