wlan-ap-Telecominfraproject/feeds/wifi-ax/mac80211/patches/qca/189-ath11k-add-support-for-device-recovery.patch
John Crispin 8cd26b4b50 ipq807x: update to 11.4-CS
Signed-off-by: John Crispin <john@phrozen.org>
2021-09-14 09:16:23 +02:00

200 lines
5.8 KiB
Diff

From 07ce203ddee206785244a083ba030ed99af07392 Mon Sep 17 00:00:00 2001
From: Anilkumar Kolli <akolli@codeaurora.org>
Date: Wed, 9 Sep 2020 19:53:01 +0530
Subject: [PATCH 13/15] ath11k: ath11k: add support for device recovery
Test command:
echo assert > /sys/kernel/debug/ath11k/qca6390/simulate_fw_crash
Then ath11k_mhi_notify_status receive MHI_CB_EE_RDDM notify and start
to do recovery process.
It need add workqueue_aux, because ab->workqueue is used when receive
ATH11K_QMI_EVENT_FW_READY in recovery process(queue_work(ab->workqueue,
&ab->restart_work;)), and ath11k_core_reset will wait for max
ATH11K_RESET_TIMEOUT_HZ for the previous restart_work finished, if
ath11k_core_reset also queued in ab->workqueue, then it will delay
restart_work of previous recovery and lead previous recovery fail.
With this patch, ath11k recovery success
Signed-off-by: Wen Gong <wgong@codeaurora.org>
---
drivers/net/wireless/ath/ath11k/core.c | 48 ++++++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath11k/core.h | 6 +++++
drivers/net/wireless/ath/ath11k/mhi.c | 31 ++++++++++++++++++++++
drivers/net/wireless/ath/ath11k/pci.c | 1 +
4 files changed, 86 insertions(+)
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -1075,9 +1075,49 @@ static void ath11k_core_restart(struct w
}
mutex_unlock(&ar->conf_mutex);
}
+
+ if (ab->is_reset) {
+ atomic_dec(&ab->reset_count);
+ complete(&ab->reset_complete);
+ ab->is_reset = false;
+ }
+
complete(&ab->driver_recovery);
}
+static void ath11k_core_reset(struct work_struct *work)
+{
+ struct ath11k_base *ab = container_of(work, struct ath11k_base, reset_work);
+ int reset_count;
+ long time_left;
+
+ reset_count = atomic_add_return(1, &ab->reset_count);
+
+ if (reset_count > 1) {
+ ath11k_warn(ab, "already reseting count %d\n", reset_count);
+
+ reinit_completion(&ab->reset_complete);
+ time_left = wait_for_completion_timeout(&ab->reset_complete,
+ ATH11K_RESET_TIMEOUT_HZ);
+ ath11k_info(ab, "reset wait time left %ld\n", time_left);
+
+ if (time_left) {
+ ath11k_info(ab, "to skip reset\n");
+ atomic_dec(&ab->reset_count);
+ return;
+ }
+ }
+
+ ath11k_info(ab, "reset starting\n");
+
+ ab->is_reset = true;
+
+ ath11k_hif_power_down(ab);
+ ath11k_hif_power_up(ab);
+
+ ath11k_info(ab, "reset started\n");
+}
+
static int ath11k_init_hw_params(struct ath11k_base *ab)
{
const struct ath11k_hw_params *hw_params = NULL;
@@ -1179,14 +1219,20 @@ struct ath11k_base *ath11k_core_alloc(st
if (!ab->workqueue)
goto err_sc_free;
+ ab->workqueue_aux = create_singlethread_workqueue("ath11k_aux_wq");
+ if (!ab->workqueue_aux)
+ goto err_free_wq;
+
mutex_init(&ab->core_lock);
spin_lock_init(&ab->base_lock);
+ init_completion(&ab->reset_complete);
INIT_LIST_HEAD(&ab->peers);
init_waitqueue_head(&ab->peer_mapping_wq);
init_waitqueue_head(&ab->wmi_ab.tx_credits_wq);
init_waitqueue_head(&ab->qmi.cold_boot_waitq);
INIT_WORK(&ab->restart_work, ath11k_core_restart);
+ INIT_WORK(&ab->reset_work, ath11k_core_reset);
timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0);
init_completion(&ab->htc_suspend);
init_completion(&ab->wow.wakeup_completed);
@@ -1197,6 +1243,8 @@ struct ath11k_base *ath11k_core_alloc(st
return ab;
+err_free_wq:
+ destroy_workqueue(ab->workqueue);
err_sc_free:
kfree(ab);
return NULL;
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -36,6 +36,7 @@
/* Pending management packets threshold for dropping probe responses */
#define ATH11K_PRB_RSP_DROP_THRESHOLD ((ATH11K_TX_MGMT_TARGET_MAX_SUPPORT_WMI * 3) / 4)
+#define ATH11K_RESET_TIMEOUT_HZ (10 * HZ)
#define ATH11K_INVALID_HW_MAC_ID 0xFF
#define ATH11K_RX_RATE_TABLE_NUM 320
#define ATH11K_RX_RATE_TABLE_11AX_NUM 576
@@ -819,6 +820,11 @@ struct ath11k_base {
struct completion driver_recovery;
struct workqueue_struct *workqueue;
struct work_struct restart_work;
+ struct workqueue_struct *workqueue_aux;
+ struct work_struct reset_work;
+ atomic_t reset_count;
+ bool is_reset;
+ struct completion reset_complete;
struct {
/* protected by data_lock */
u32 fw_crash_counter;
--- a/drivers/net/wireless/ath/ath11k/mhi.c
+++ b/drivers/net/wireless/ath/ath11k/mhi.c
@@ -281,6 +281,29 @@ static int ath11k_mhi_get_msi(struct ath
return 0;
}
+static char *ath11k_mhi_callback_to_str(int reason)
+{
+ switch (reason) {
+ case MHI_CB_IDLE:
+ return "MHI_CB_IDLE";
+ case MHI_CB_PENDING_DATA:
+ return "MHI_CB_PENDING_DATA";
+ case MHI_CB_LPM_ENTER:
+ return "MHI_CB_LPM_ENTER";
+ case MHI_CB_LPM_EXIT:
+ return "MHI_CB_LPM_EXIT";
+ case MHI_CB_EE_RDDM:
+ return "MHI_CB_EE_RDDM";
+ case MHI_CB_SYS_ERROR:
+ return "MHI_CB_SYS_ERROR";
+ case MHI_CB_FATAL_ERROR:
+ return "MHI_CB_FATAL_ERROR";
+ default:
+ return "UNKNOWN";
+ }
+};
+
+
static int ath11k_mhi_op_runtime_get(struct mhi_controller *mhi_cntrl)
{
return 0;
@@ -288,6 +311,7 @@ static int ath11k_mhi_op_runtime_get(str
static void ath11k_mhi_op_runtime_put(struct mhi_controller *mhi_cntrl)
{
+
}
static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,
@@ -295,10 +319,16 @@ static void ath11k_mhi_op_status_cb(stru
{
struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev);
+ ath11k_info(ab, "mhi notify status reason %s\n",
+ ath11k_mhi_callback_to_str(cb));
+
switch (cb) {
case MHI_CB_SYS_ERROR:
ath11k_warn(ab, "firmware crashed: MHI_CB_SYS_ERROR\n");
break;
+ case MHI_CB_EE_RDDM:
+ queue_work(ab->workqueue_aux, &ab->reset_work);
+ break;
default:
break;
}
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -1334,6 +1334,7 @@ static void ath11k_pci_remove(struct pci
}
set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags);
+ cancel_work_sync(&ab->reset_work);
ath11k_core_deinit(ab);