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:
Vasanthakumar Thiagarajan 2012-08-29 19:40:26 +05:30 committed by Kalle Valo
parent ede615d2f0
commit 84caf8005b
10 changed files with 126 additions and 3 deletions

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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 */

View File

@ -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 */
};

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}