diff mbox series

[RFC,v1,048/256] cl8k: add coredump.c

Message ID 20210617160223.160998-49-viktor.barna@celeno.com
State New
Headers show
Series wireless: cl8k driver for Celeno IEEE 802.11ax devices | expand

Commit Message

Viktor Barna June 17, 2021, 3:58 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/coredump.c | 190 ++++++++++++++++++++
 1 file changed, 190 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/coredump.c

--
2.30.0
diff mbox series

Patch

diff --git a/drivers/net/wireless/celeno/cl8k/coredump.c b/drivers/net/wireless/celeno/cl8k/coredump.c
new file mode 100644
index 000000000000..bf0313715f6f
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/coredump.c
@@ -0,0 +1,190 @@ 
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "coredump.h"
+#include "recovery.h"
+#include "mib.h"
+#include "ela.h"
+#ifdef CONFIG_CL_PCIE
+#include "bus/pci/ipc.h"
+#endif
+#include "chip.h"
+#include "config.h"
+
+#include "fw/fw_dbg.h"
+
+#include <linux/devcoredump.h>
+
+static int cl_coredump_generate(struct cl_hw *cl_hw)
+{
+       struct cl_coredump *dump;
+
+       dump = cl_fw_dbg_prepare_coredump(cl_hw);
+       if (!dump)
+               return -ENODATA;
+
+       dev_coredumpv(cl_hw->chip->dev, dump, le32_to_cpu(dump->len),
+                     GFP_KERNEL);
+
+       return 0;
+}
+
+static void cl_coredump_done(struct cl_hw *cl_hw)
+{
+       /*
+        * Print MIB counters only if watchdog is disabled,
+        * otherwise the dump of prints effects the recovery
+        */
+       if (cl_hw->conf->ce_fw_watchdog_mode == FW_WD_DISABLE)
+               cl_mib_cntrs_dump(cl_hw);
+
+       if (!test_bit(CL_DEV_STARTED, &cl_hw->drv_flags))
+               return;
+
+       /*
+        * Assuming firmware cannot request next dump before we release the host buffer
+        *  so no need to sync the following against error_ind()
+        */
+       cl_hw->debugfs.trace_prst = false;
+#ifdef CONFIG_CL_PCIE
+       cl_ipc_dbginfobuf_push(cl_hw->ipc_env, cl_hw->dbginfo.dma_addr);
+#endif
+       if (cl_hw->dbginfo.buf->u.dump.general_data.error_type == DBG_ERROR_FATAL ||
+           cl_hw->assert_info.restart_needed) {
+               cl_dbg_err(cl_hw, "Starting recovery due to unrecoverable assert\n");
+               cl_recovery_start(cl_hw, RECOVERY_UNRECOVERABLE_ASSERT);
+       }
+}
+
+static void cl_coredump_work(struct work_struct *ws)
+{
+       struct cl_debugfs *debugfs = container_of(ws, struct cl_debugfs, coredump_work);
+       struct cl_hw *cl_hw = container_of(debugfs, struct cl_hw, debugfs);
+       unsigned long flags;
+
+       debugfs->coredump_call_tstamp = jiffies;
+
+       cl_coredump_generate(cl_hw);
+       if (cl_ela_is_on(cl_hw->chip)) {
+               cl_ela_lcu_reset(cl_hw->chip);
+               cl_ela_lcu_apply_config(cl_hw->chip);
+       }
+
+       spin_lock_irqsave(&debugfs->coredump_lock, flags);
+       if (!debugfs->unregistering)
+               cl_coredump_done(cl_hw);
+       debugfs->coredump_scheduled = false;
+       spin_unlock_irqrestore(&debugfs->coredump_lock, flags);
+}
+
+int cl_coredump_trigger(struct cl_hw *cl_hw)
+{
+       struct cl_debugfs *debugfs = &cl_hw->debugfs;
+       unsigned long flags;
+       unsigned long curr_time = jiffies;
+       unsigned int diff_time = jiffies_to_msecs(curr_time - debugfs->coredump_call_tstamp);
+
+       if (diff_time < cl_hw->conf->ci_coredump_diff_time_ms) {
+#ifdef CONFIG_CL_PCIE
+               cl_ipc_dbginfobuf_push(cl_hw->ipc_env, cl_hw->dbginfo.dma_addr);
+#endif
+               cl_dbg_verbose(cl_hw,
+                              "Skip coredump - time from previous call=%u m-sec\n",
+                              diff_time);
+               return -1;
+       }
+
+       spin_lock_irqsave(&debugfs->coredump_lock, flags);
+       if (debugfs->coredump_scheduled) {
+               spin_unlock_irqrestore(&debugfs->coredump_lock, flags);
+               cl_dbg_verbose(cl_hw, ": Already scheduled\n");
+               return -EBUSY;
+       }
+
+       if (debugfs->unregistering) {
+               spin_unlock_irqrestore(&debugfs->coredump_lock, flags);
+               cl_dbg_verbose(cl_hw, ": unregistering\n");
+               return -ENOENT;
+       }
+
+       debugfs->coredump_scheduled = true;
+       debugfs->trace_prst = true;
+       ktime_get_real_ts64(&cl_hw->dbginfo.trigger_tstamp);
+
+       schedule_work(&debugfs->coredump_work);
+       spin_unlock_irqrestore(&debugfs->coredump_lock, flags);
+
+       return 0;
+}
+
+bool cl_coredump_recovery(struct cl_hw *cl_hw, int reason)
+{
+       struct cl_debugfs *debugfs = &cl_hw->debugfs;
+       unsigned long flags;
+       bool need_restart = false;
+
+       spin_lock_irqsave(&debugfs->coredump_lock, flags);
+
+       if (!debugfs->coredump_scheduled) {
+               cl_dbg_trace(cl_hw,
+                            "Starting recovery due to reason:%d\n",
+                            reason);
+               cl_recovery_start(cl_hw, reason);
+       } else {
+               need_restart = true;
+       }
+
+       spin_unlock_irqrestore(&debugfs->coredump_lock, flags);
+
+       return need_restart;
+}
+
+bool cl_coredump_is_scheduled(struct cl_hw *cl_hw)
+{
+       struct cl_debugfs *debugfs = &cl_hw->debugfs;
+
+       return debugfs->coredump_scheduled;
+}
+
+void cl_coredump_reset_trace(struct cl_hw *cl_hw)
+{
+       struct cl_debugfs *debugfs = &cl_hw->debugfs;
+
+       debugfs->trace_prst = false;
+}
+
+void cl_coredump_init(struct cl_hw *cl_hw, struct dentry *dir_drv)
+{
+       struct cl_debugfs *debugfs = &cl_hw->debugfs;
+
+       debugfs->dir = dir_drv;
+       debugfs->unregistering = false;
+       debugfs->trace_prst = false;
+       debugfs->coredump_scheduled = false;
+
+       INIT_WORK(&debugfs->coredump_work, cl_coredump_work);
+
+       spin_lock_init(&debugfs->coredump_lock);
+
+       /*
+        * Initialize coredump_call_tstamp to current time minus
+        * (ci_coredump_diff_time_ms + 1), so that if assert happens immediately
+        * coredump will be called.
+        */
+       debugfs->coredump_call_tstamp = jiffies -
+               msecs_to_jiffies(cl_hw->conf->ci_coredump_diff_time_ms + 1);
+}
+
+void cl_coredump_close(struct cl_hw *cl_hw)
+{
+       struct cl_debugfs *debugfs = &cl_hw->debugfs;
+
+       flush_work(&debugfs->coredump_work);
+
+       if (!debugfs->dir)
+               return;
+
+       debugfs->unregistering = true;
+       debugfs_remove_recursive(debugfs->dir);
+       debugfs->dir = NULL;
+}