ath6kl: Recover from fw crash
Re-initialize the target when fw crash is reported. This would make the device functional again after target crash. During the target re-initialization it is made sure that target is not bugged with data/cmd request, ar->state ATH6KL_STATE_RECOVERY is used for this purpose. Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
This commit is contained in:
parent
ede615d2f0
commit
84caf8005b
|
@ -34,6 +34,7 @@ ath6kl_core-y += main.o
|
|||
ath6kl_core-y += txrx.o
|
||||
ath6kl_core-y += wmi.o
|
||||
ath6kl_core-y += core.o
|
||||
ath6kl_core-y += recovery.o
|
||||
ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o
|
||||
|
||||
obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o
|
||||
|
|
|
@ -2503,14 +2503,23 @@ static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
|
|||
{
|
||||
struct ath6kl *ar = wiphy_priv(wiphy);
|
||||
|
||||
ath6kl_recovery_suspend(ar);
|
||||
|
||||
return ath6kl_hif_suspend(ar, wow);
|
||||
}
|
||||
|
||||
static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
|
||||
{
|
||||
struct ath6kl *ar = wiphy_priv(wiphy);
|
||||
int err;
|
||||
|
||||
return ath6kl_hif_resume(ar);
|
||||
err = ath6kl_hif_resume(ar);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ar->fw_recovery.enable = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3434,6 +3443,10 @@ void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
|
|||
clear_bit(CONNECTED, &vif->flags);
|
||||
clear_bit(CONNECT_PEND, &vif->flags);
|
||||
|
||||
/* Stop netdev queues, needed during recovery */
|
||||
netif_stop_queue(vif->ndev);
|
||||
netif_carrier_off(vif->ndev);
|
||||
|
||||
/* disable scanning */
|
||||
if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
|
||||
|
@ -3447,7 +3460,7 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
|
|||
struct ath6kl_vif *vif;
|
||||
|
||||
vif = ath6kl_vif_first(ar);
|
||||
if (!vif) {
|
||||
if (!vif && ar->state != ATH6KL_STATE_RECOVERY) {
|
||||
/* save the current power mode before enabling power save */
|
||||
ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
|
||||
|
||||
|
|
|
@ -202,6 +202,8 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type)
|
|||
ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n",
|
||||
__func__, wdev->netdev->name, wdev->netdev, ar);
|
||||
|
||||
ath6kl_recovery_init(ar);
|
||||
|
||||
return ret;
|
||||
|
||||
err_rxbuf_cleanup:
|
||||
|
@ -291,6 +293,8 @@ void ath6kl_core_cleanup(struct ath6kl *ar)
|
|||
{
|
||||
ath6kl_hif_power_off(ar);
|
||||
|
||||
ath6kl_recovery_cleanup(ar);
|
||||
|
||||
destroy_workqueue(ar->ath6kl_wq);
|
||||
|
||||
if (ar->htc_target)
|
||||
|
|
|
@ -645,6 +645,12 @@ enum ath6kl_state {
|
|||
ATH6KL_STATE_DEEPSLEEP,
|
||||
ATH6KL_STATE_CUTPOWER,
|
||||
ATH6KL_STATE_WOW,
|
||||
ATH6KL_STATE_RECOVERY,
|
||||
};
|
||||
|
||||
/* Fw error recovery */
|
||||
enum ath6kl_fw_err {
|
||||
ATH6KL_FW_ASSERT,
|
||||
};
|
||||
|
||||
struct ath6kl {
|
||||
|
@ -790,6 +796,12 @@ struct ath6kl {
|
|||
|
||||
bool wiphy_registered;
|
||||
|
||||
struct ath6kl_fw_recovery {
|
||||
bool enable;
|
||||
struct work_struct recovery_work;
|
||||
unsigned long err_reason;
|
||||
} fw_recovery;
|
||||
|
||||
#ifdef CONFIG_ATH6KL_DEBUG
|
||||
struct {
|
||||
struct sk_buff_head fwlog_queue;
|
||||
|
@ -925,4 +937,10 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type);
|
|||
void ath6kl_core_cleanup(struct ath6kl *ar);
|
||||
void ath6kl_core_destroy(struct ath6kl *ar);
|
||||
|
||||
/* Fw error recovery */
|
||||
void ath6kl_init_hw_restart(struct ath6kl *ar);
|
||||
void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason);
|
||||
void ath6kl_recovery_init(struct ath6kl *ar);
|
||||
void ath6kl_recovery_cleanup(struct ath6kl *ar);
|
||||
void ath6kl_recovery_suspend(struct ath6kl *ar);
|
||||
#endif /* CORE_H */
|
||||
|
|
|
@ -44,6 +44,7 @@ enum ATH6K_DEBUG_MASK {
|
|||
ATH6KL_DBG_SUSPEND = BIT(20),
|
||||
ATH6KL_DBG_USB = BIT(21),
|
||||
ATH6KL_DBG_USB_BULK = BIT(22),
|
||||
ATH6KL_DBG_RECOVERY = BIT(23),
|
||||
ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */
|
||||
};
|
||||
|
||||
|
|
|
@ -136,6 +136,7 @@ static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
|
|||
|
||||
ath6kl_hif_dump_fw_crash(dev->ar);
|
||||
ath6kl_read_fwlogs(dev->ar);
|
||||
ath6kl_recovery_err_notify(dev->ar, ATH6KL_FW_ASSERT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1695,6 +1695,25 @@ int ath6kl_init_hw_stop(struct ath6kl *ar)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void ath6kl_init_hw_restart(struct ath6kl *ar)
|
||||
{
|
||||
|
||||
ar->state = ATH6KL_STATE_RECOVERY;
|
||||
|
||||
ath6kl_cfg80211_stop_all(ar);
|
||||
|
||||
if (__ath6kl_init_hw_stop(ar))
|
||||
return;
|
||||
|
||||
if (__ath6kl_init_hw_start(ar)) {
|
||||
ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ar->state = ATH6KL_STATE_ON;
|
||||
ar->fw_recovery.err_reason = 0;
|
||||
}
|
||||
|
||||
/* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */
|
||||
void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Qualcomm Atheros, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
#include "cfg80211.h"
|
||||
#include "debug.h"
|
||||
|
||||
static void ath6kl_recovery_work(struct work_struct *work)
|
||||
{
|
||||
struct ath6kl *ar = container_of(work, struct ath6kl,
|
||||
fw_recovery.recovery_work);
|
||||
|
||||
ath6kl_init_hw_restart(ar);
|
||||
}
|
||||
|
||||
void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason)
|
||||
{
|
||||
ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Fw error detected, reason:%d\n",
|
||||
reason);
|
||||
|
||||
set_bit(reason, &ar->fw_recovery.err_reason);
|
||||
|
||||
if (ar->fw_recovery.enable && ar->state != ATH6KL_STATE_RECOVERY)
|
||||
queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work);
|
||||
}
|
||||
|
||||
void ath6kl_recovery_init(struct ath6kl *ar)
|
||||
{
|
||||
struct ath6kl_fw_recovery *recovery = &ar->fw_recovery;
|
||||
|
||||
recovery->enable = true;
|
||||
INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work);
|
||||
}
|
||||
|
||||
void ath6kl_recovery_cleanup(struct ath6kl *ar)
|
||||
{
|
||||
ar->fw_recovery.enable = false;
|
||||
|
||||
cancel_work_sync(&ar->fw_recovery.recovery_work);
|
||||
}
|
||||
|
||||
void ath6kl_recovery_suspend(struct ath6kl *ar)
|
||||
{
|
||||
ath6kl_recovery_cleanup(ar);
|
||||
|
||||
/* Process pending fw error detection */
|
||||
if (ar->fw_recovery.err_reason)
|
||||
ath6kl_init_hw_restart(ar);
|
||||
}
|
|
@ -931,6 +931,9 @@ static int ath6kl_sdio_resume(struct ath6kl *ar)
|
|||
|
||||
case ATH6KL_STATE_RESUMING:
|
||||
break;
|
||||
|
||||
case ATH6KL_STATE_RECOVERY:
|
||||
break;
|
||||
}
|
||||
|
||||
ath6kl_cfg80211_resume(ar);
|
||||
|
|
|
@ -288,7 +288,8 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb,
|
|||
int status = 0;
|
||||
struct ath6kl_cookie *cookie = NULL;
|
||||
|
||||
if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) {
|
||||
if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW) ||
|
||||
ar->state == ATH6KL_STATE_RECOVERY) {
|
||||
dev_kfree_skb(skb);
|
||||
return -EACCES;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue