diff mbox series

[RFC,v1,145/256] cl8k: add recovery.c

Message ID 20210617160223.160998-146-viktor.barna@celeno.com (mailing list archive)
State RFC
Delegated to: Kalle Valo
Headers show
Series wireless: cl8k driver for Celeno IEEE 802.11ax devices | expand

Commit Message

Viktor Barna June 17, 2021, 4 p.m. UTC
From: Viktor Barna <viktor.barna@celeno.com>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@celeno.com>
---
 drivers/net/wireless/celeno/cl8k/recovery.c | 264 ++++++++++++++++++++
 1 file changed, 264 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/recovery.c

--
2.30.0
diff mbox series

Patch

diff --git a/drivers/net/wireless/celeno/cl8k/recovery.c b/drivers/net/wireless/celeno/cl8k/recovery.c
new file mode 100644
index 000000000000..1bba86ea4882
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/recovery.c
@@ -0,0 +1,264 @@ 
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "recovery.h"
+#include "rx/rx_amsdu.h"
+#include "main.h"
+#include "phy/phy.h"
+#include "vif.h"
+#include "dfs/dfs.h"
+#include "maintenance.h"
+#include "tx/tx_inject.h"
+#include "ext/dyn_mcast_rate.h"
+#include "ext/dyn_bcast_rate.h"
+#include "vns.h"
+#include "radio.h"
+#include "wrs/wrs_api.h"
+#include "config.h"
+#include "fw/msg_tx.h"
+#include "coredump.h"
+#ifdef CONFIG_CL_PCIE
+#include "bus/pci/irq.h"
+#endif
+
+struct cl_recovery_work {
+       struct work_struct ws;
+       struct cl_hw *cl_hw;
+       int reason;
+};
+
+#define RECOVERY_POLL_TIMEOUT 6
+
+static void cl_recovery_poll_completion(struct cl_hw *cl_hw)
+{
+       u8 cntr = 0;
+
+       while (test_bit(CL_DEV_SW_RESTART, &cl_hw->drv_flags)) {
+               msleep(1000);
+
+               if (++cntr == RECOVERY_POLL_TIMEOUT) {
+                       cl_dbg_verbose(cl_hw, "\n");
+                       cl_dbg_err(cl_hw, "Driver handgup was detected!...");
+                       break;
+               }
+       }
+}
+
+static void cl_recovery_start_hw(struct cl_hw *cl_hw)
+{
+       clear_bit(CL_DEV_STOP_HW, &cl_hw->drv_flags);
+
+       /* Restart MAC firmware... */
+       if (cl_main_on(cl_hw)) {
+               cl_dbg_err(cl_hw, "Couldn't turn platform on .. aborting\n");
+               return;
+       }
+
+       if (cl_msg_tx_reset(cl_hw)) {
+               cl_dbg_err(cl_hw, "Failed to send firmware reset .. aborting\n");
+               return;
+       }
+
+       set_bit(CL_DEV_SW_RESTART, &cl_hw->drv_flags);
+       clear_bit(CL_DEV_HW_RESTART, &cl_hw->drv_flags);
+
+       /* Hand over to mac80211 from here */
+       ieee80211_restart_hw(cl_hw->hw);
+
+       cl_recovery_poll_completion(cl_hw);
+}
+
+static void cl_recovery_stop_hw(struct cl_hw *cl_hw)
+{
+       /* Start recovery process */
+       cl_hw->recovery_db.in_recovery = true;
+
+       clear_bit(CL_DEV_STARTED, &cl_hw->drv_flags);
+       set_bit(CL_DEV_HW_RESTART, &cl_hw->drv_flags);
+       set_bit(CL_DEV_STOP_HW, &cl_hw->drv_flags);
+#ifdef CONFIG_CL_PCIE
+       /* Disable interrupts */
+       cl_irq_disable(cl_hw, cl_hw->ipc_e2a_irq.all);
+#endif
+       cl_tx_inject_stop_in_recovery(cl_hw);
+       cl_maintenance_stop(cl_hw);
+
+       ieee80211_stop_queues(cl_hw->hw);
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+
+       cl_main_off(cl_hw);
+
+       cl_hw->fw_active = false;
+       cl_hw->fw_send_start = false;
+
+       cl_coredump_reset_trace(cl_hw);
+
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+
+       /* Reset it so MM_SET_FILTER_REQ will be called during the recovery */
+       cl_hw->rx_filter = 0;
+
+       /*
+        * Reset channel/frequency parameters so that cl_msg_tx_set_channel()
+        * will not be skipped in cl_ops_config()
+        */
+       cl_hw->channel = 0;
+       cl_hw->primary_freq = 0;
+       cl_hw->center_freq = 0;
+}
+
+static void cl_recovery_process(struct cl_hw *cl_hw)
+{
+       int ret;
+
+       cl_dbg_verbose(cl_hw, "Start\n");
+
+       cl_recovery_stop_hw(cl_hw);
+       cl_phy_reset(cl_hw);
+
+       ret = cl_phy_load_recovery(cl_hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "cl_phy_load_recovery failed %d\n", ret);
+               return;
+       }
+
+       cl_recovery_start_hw(cl_hw);
+}
+
+static void cl_recovery_handler(struct cl_hw *cl_hw, int reason)
+{
+       unsigned long recovery_diff = jiffies_to_msecs(jiffies - cl_hw->recovery_db.last_restart);
+
+       cl_hw->recovery_db.restart_cnt++;
+
+       if (recovery_diff > cl_hw->conf->ce_fw_watchdog_limit_time) {
+               cl_hw->recovery_db.restart_cnt = 1;
+       } else if (cl_hw->recovery_db.restart_cnt > cl_hw->conf->ce_fw_watchdog_limit_count) {
+               cl_dbg_verbose(cl_hw, "Too many failures... aborting\n");
+               cl_hw->conf->ce_fw_watchdog_mode = FW_WD_DISABLE;
+               return;
+       }
+
+       cl_hw->recovery_db.last_restart = jiffies;
+
+       /* Count recovery attempts for statistics */
+       cl_hw->fw_recovery_cntr++;
+       cl_dbg_trace(cl_hw, "Recovering from firmware failure, attempt #%i\n",
+                    cl_hw->fw_recovery_cntr);
+
+       cl_recovery_process(cl_hw);
+}
+
+static void cl_recovery_work_do(struct work_struct *ws)
+{
+       /* Worker for restarting hw. */
+       struct cl_recovery_work *recovery_work = container_of(ws, struct cl_recovery_work, ws);
+
+       recovery_work->cl_hw->assert_info.restart_sched = false;
+       cl_recovery_handler(recovery_work->cl_hw, recovery_work->reason);
+       kfree(recovery_work);
+}
+
+static void cl_recovery_work_sched(struct cl_hw *cl_hw, int reason)
+{
+       /*
+        * Schedule work to restart device and firmware
+        * This is scheduled when driver detects hw assert storm.
+        */
+       struct cl_recovery_work *recovery_work;
+
+       if (!cl_hw->ipc_env || cl_hw->is_stop_context) {
+               cl_dbg_warn(cl_hw, "Skip recovery - Running down!\n");
+               return;
+       }
+
+       /* If restart is already scheduled - exit */
+       if (cl_hw->assert_info.restart_sched)
+               return;
+
+       cl_hw->assert_info.restart_sched = true;
+
+       /* Recovery_work will be freed by cl_recovery_work_do */
+       recovery_work = kzalloc(sizeof(*recovery_work), GFP_ATOMIC);
+
+       if (!recovery_work)
+               return;
+
+       INIT_WORK(&recovery_work->ws, cl_recovery_work_do);
+       recovery_work->cl_hw = cl_hw;
+       recovery_work->reason = reason;
+
+       queue_work(cl_hw->drv_workqueue, &recovery_work->ws);
+}
+
+bool cl_recovery_in_progress(struct cl_hw *cl_hw)
+{
+       return cl_hw->recovery_db.in_recovery;
+}
+
+void cl_recovery_reconfig_complete(struct cl_hw *cl_hw)
+{
+       clear_bit(CL_DEV_SW_RESTART, &cl_hw->drv_flags);
+
+       /* Multicast/Broadcast rate recovery */
+       cl_dyn_mcast_rate_recovery(cl_hw);
+       cl_dyn_bcast_rate_recovery(cl_hw);
+
+       /* DFS recovery */
+       cl_dfs_recovery(cl_hw);
+
+       /* VNS recovery */
+       cl_vns_recovery(cl_hw);
+
+       /* Restore EDCA configuration */
+       cl_edca_recovery(cl_hw);
+
+       /* Temperature  recovery */
+       cl_temperature_recovery(cl_hw);
+
+       /* Sounding recovery */
+       cl_sounding_recovery(cl_hw);
+
+       /*
+        * Update Tx params for all connected stations to sync firmware after the
+        * recovery process.
+        */
+       cl_wrs_api_recovery(cl_hw);
+
+       /* Enable maintenance timers back */
+       cl_maintenance_start(cl_hw);
+
+       if (cl_radio_is_on(cl_hw))
+               cl_msg_tx_set_idle(cl_hw, MAC_ACTIVE);
+
+       cl_hw->recovery_db.in_recovery = false;
+
+       pr_debug("cl_recovery: complete\n");
+
+       cl_rx_post_recovery(cl_hw);
+}
+
+void cl_recovery_start(struct cl_hw *cl_hw, int reason)
+{
+       /* Prevent new messages to be sent until firmware has recovered */
+       set_bit(CL_DEV_FW_ERROR, &cl_hw->drv_flags);
+
+       switch (cl_hw->conf->ce_fw_watchdog_mode) {
+       case FW_WD_DISABLE:
+               cl_dbg_info(cl_hw, "Skip recovery - Watchdog is off!\n");
+               break;
+
+       case FW_WD_INTERNAL_RECOVERY:
+               cl_recovery_work_sched(cl_hw, reason);
+               break;
+
+       case FW_WD_DRV_RELOAD:
+               /* TODO: Implement netlink hint to the userspace */
+               cl_dbg_info(cl_hw, "RELOAD handler is absent, doing nothing");
+               break;
+
+       default:
+               break;
+       }
+}