new file mode 100644
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "hw_assert.h"
+#include "recovery.h"
+#include "debug.h"
+#include "dbgfile.h"
+#include "bus/pci/ipc.h"
+#include "coredump.h"
+
+#define ASSERT_PATTERN 0xC0DEDEAD
+
+/*
+ * Function will take time stamp for each hw error indication.
+ * when time diff between each error is less than ce_hw_assert_time_max
+ * cl_hw_restart work will be scheduled
+ */
+static bool cl_hw_assert_storm_detect(struct cl_hw *cl_hw)
+{
+ struct cl_hw_asserts_info *assert_info = &cl_hw->assert_info;
+ u8 idx = assert_info->index % CL_MIN_ASSERT_CNT;
+ /* Get the oldest assert timestamp. */
+ u8 prev_idx = (assert_info->index + 1) % CL_MIN_ASSERT_CNT;
+ bool is_hw_restarted = false;
+
+ if (assert_info->restart_sched) {
+ is_hw_restarted = true;
+ } else {
+ /* Take time stamp of the assert */
+ assert_info->timestamp[idx] = jiffies;
+ assert_info->index++;
+ /* In case hw assert time diff is less than CL_HW_ASSERT_TIME_MAX, restart hw. */
+ if (assert_info->index > CL_MIN_ASSERT_CNT) {
+ unsigned long time_diff_jiffies =
+ assert_info->timestamp[idx] - assert_info->timestamp[prev_idx];
+ unsigned int time_diff_msecs = jiffies_to_msecs(time_diff_jiffies);
+
+ if (time_diff_msecs < cl_hw->conf->ce_hw_assert_time_max) {
+ assert_info->index = 0;
+
+ cl_dbg_err(cl_hw, "Assert storm detect (time_diff = %u)\n",
+ time_diff_msecs);
+ cl_recovery_start(cl_hw, RECOVERY_ASSERT_STORM_DETECT);
+
+ is_hw_restarted = true;
+ }
+ }
+ }
+
+ return is_hw_restarted;
+}
+
+void cl_hw_assert_info_init(struct cl_hw *cl_hw)
+{
+ memset(&cl_hw->assert_info, 0, sizeof(cl_hw->assert_info));
+}
+
+void cl_hw_assert_print(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ struct dbg_print_ind *ind = (struct dbg_print_ind *)msg->param;
+ const char *assert_string;
+ u32 assert_pattern;
+ u16 file_id = le16_to_cpu(ind->file_id);
+ u16 line = le16_to_cpu(ind->line);
+ u16 has_param = le16_to_cpu(ind->has_param);
+ u32 param = le32_to_cpu(ind->param);
+
+ /* If ce_hw_assert_time_max is 0, HW assert storm detection is disabled */
+ if (cl_hw->conf->ce_hw_assert_time_max)
+ if (cl_hw_assert_storm_detect(cl_hw))
+ return;
+
+ /* Print ASSERT message with fileid, line, [parameter] */
+ if (has_param)
+ cl_dbg_err(cl_hw, "ASSERT_TCV%u @ FILE=%hu LINE=%hu param=0x%08X\n",
+ cl_hw->idx, file_id, line, param);
+ else
+ cl_dbg_err(cl_hw, "ASSERT_TCV%u @ file=%hu line=%hu\n",
+ cl_hw->idx, file_id, line);
+
+ assert_string = cl_dbgfile_get_msg_txt(&cl_hw->dbg_data, file_id, line);
+
+ if (!assert_string)
+ assert_string = "ASSERT STRING NOT FOUND";
+
+ /* TODO: length of single print may be limited, consider printing long msgs by pieces */
+ cl_dbg_err(cl_hw, "%.500s\n", assert_string);
+
+ assert_pattern = le32_to_cpu(cl_hw->ipc_env->shared->assert_pattern);
+
+ /* Reset ASSERT pattern if needed (in order to prevent assert prints loop) */
+ if (assert_pattern == ASSERT_PATTERN)
+ cl_hw->ipc_env->shared->assert_pattern = 0;
+
+ if (ind->err_no_dump) {
+ bool reason = RECOVERY_UNRECOVERABLE_ASSERT_NO_DUMP;
+ bool restart = cl_coredump_recovery(cl_hw, reason);
+
+ if (restart)
+ cl_hw->assert_info.restart_needed = true;
+ } else {
+ cl_hw->assert_info.restart_needed = false;
+ }
+}
+
+void cl_hw_assert_check(struct cl_hw *cl_hw)
+{
+ struct cl_ipc_shared_env *shared_env = cl_hw->ipc_env->shared;
+ u32 assert_pattern = le32_to_cpu(shared_env->assert_pattern);
+
+ if (assert_pattern == ASSERT_PATTERN) {
+ u16 line = le16_to_cpu(shared_env->assert_line_num);
+ u16 fileid = le16_to_cpu(shared_env->assert_file_id);
+ u32 param = le32_to_cpu(shared_env->assert_param);
+ const char *assert_string = cl_dbgfile_get_msg_txt(&cl_hw->dbg_data, fileid, line);
+
+ /* Print 1st ASSERT message with fileid, line, [parameter] */
+ cl_dbg_err(cl_hw, "ASSERT_%cmac @ FILE=%hu LINE=%hu param=0x%08X\n",
+ cl_hw->fw_prefix, fileid, line, param);
+
+ if (!assert_string)
+ assert_string = "ASSERT STRING NOT FOUND";
+
+ cl_dbg_err(cl_hw, "%.500s\n", assert_string);
+
+ /* Reset ASSERT pattern in order to prevent assert prints loop */
+ shared_env->assert_pattern = 0;
+ }
+}