[BugFix][API-1513]add MTK && IFX patch for debug wifi driver insmod abnormal --new

Affected branch:MR3.0-xx && GSW3.0
Affected module:wifi
Is it addected on both ZXIC and MTK: only MTK
Self-test: Yes
Doc Update: No

Change-Id: Idc8944dbf1c05ccc8457fa7d249253d3aaa8a659
diff --git a/meta/meta-mediatek-mt2735/recipes-kernel/modules/files/wg870_drv_insmod.service b/meta/meta-mediatek-mt2735/recipes-kernel/modules/files/wg870_drv_insmod.service
index eb394c8..a7fb008 100755
--- a/meta/meta-mediatek-mt2735/recipes-kernel/modules/files/wg870_drv_insmod.service
+++ b/meta/meta-mediatek-mt2735/recipes-kernel/modules/files/wg870_drv_insmod.service
@@ -3,7 +3,9 @@
 
 [Service]
 Type=forking
+ExecStartPre=/bin/sh -c '/bin/echo 30 > /proc/hang_detect_monitor'
 ExecStart=/bin/sh /etc/wg870/wg870_drv_insmod.sh
+ExecStartPost=/bin/sh -c '/bin/echo 0 > /proc/hang_detect_monitor'
 ExecStopPost=/etc/wg870/wg870_drv_insmod.sh teardown
 
 [Install]
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/Kconfig b/src/kernel/linux/v4.19/drivers/misc/mediatek/Kconfig
index 9075430..c903cef 100644
--- a/src/kernel/linux/v4.19/drivers/misc/mediatek/Kconfig
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/Kconfig
@@ -369,6 +369,7 @@
 source "drivers/misc/mediatek/leds/Kconfig"
 source "drivers/misc/mediatek/dvfsrc/Kconfig"
 source "drivers/misc/mediatek/hsm/Kconfig"
+source "drivers/misc/mediatek/hangdet/Kconfig"
 endmenu # Debug
 
 menu "PCIe"
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/Makefile b/src/kernel/linux/v4.19/drivers/misc/mediatek/Makefile
index 27c40f3..b924f13 100644
--- a/src/kernel/linux/v4.19/drivers/misc/mediatek/Makefile
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/Makefile
@@ -72,6 +72,7 @@
 obj-$(CONFIG_MTK_ECCCI_C2K) += c2k_usb/
 obj-y +=eint_debug/
 obj-$(CONFIG_HSM_SUPPORT) += hsm/
+obj-$(CONFIG_MTK_AEE_HANGDET) += hangdet/
 obj-y +=wakeup_gpio/
 #tianyan@2021.10.28 modify for DTR/RI gpio start
 obj-y += wakeup_dtr/
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/hangdet/Kconfig b/src/kernel/linux/v4.19/drivers/misc/mediatek/hangdet/Kconfig
new file mode 100644
index 0000000..4587fd4
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/hangdet/Kconfig
@@ -0,0 +1,7 @@
+config MTK_AEE_HANGDET
+	tristate "Enable AEE Kernel Hang Detector"
+	help
+	  MTK_AEE_HANGDET is the kernel config of hang detector feature
+	  designed by MTK, which is the mechanism to check if each cpu
+	  is alived. When cpu hang is detected, raise excetpion.
+
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/hangdet/Makefile b/src/kernel/linux/v4.19/drivers/misc/mediatek/hangdet/Makefile
new file mode 100644
index 0000000..27cfe4c
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/hangdet/Makefile
@@ -0,0 +1,31 @@
+
+# Copyright (C) 2020 MediaTek Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+#
+
+ifeq ($(CONFIG_MTK_GCOV_KERNEL),y)
+GCOV_PROFILE := y
+endif
+
+ifdef CONFIG_MTK_AEE_FEATURE
+subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/aee/mrdump
+subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/aee/aed
+subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/aee
+endif
+subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/include
+subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
+subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/mmp/
+subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/log_store/
+subdir-ccflags-y += -I$(srctree)/include/
+subdir-ccflags-y += -I$(srctree)/kernel/
+
+obj-$(CONFIG_MTK_AEE_HANGDET)	+= aee_hangdet.o
+
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/hangdet/aee_hangdet.c b/src/kernel/linux/v4.19/drivers/misc/mediatek/hangdet/aee_hangdet.c
new file mode 100644
index 0000000..c7fe042
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/hangdet/aee_hangdet.c
@@ -0,0 +1,769 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 MediaTek Inc.
+ */
+
+#include <linux/cpu.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/sched/clock.h>
+#include <linux/spinlock.h>
+#include <linux/suspend.h>
+#include <linux/sysrq.h>
+#include <mt-plat/mboot_params.h>
+#include <uapi/linux/sched/types.h>
+#include <linux/sched/debug.h>
+#include "../../../../kernel/sched/sched.h"
+#include <linux/reboot.h>
+#include <linux/rtc.h>
+#include <asm/kexec.h>
+#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
+#include <mt-plat/mrdump.h>
+#include "mrdump_helper.h"
+#include "mrdump_private.h"
+#endif
+
+/*************************************************************************
+ * Feature configure region
+ *************************************************************************/
+#define WK_MAX_MSG_SIZE (128)
+#define SOFT_KICK_RANGE     (100*1000) // 100ms
+
+#define WDT_MODE		0x0
+#define WDT_MODE_EN		0x1
+#define WDT_STATUS		0xc
+#define WDT_STATUS_IRQ          (1 << 29)
+#define WDT_LENGTH_TIMEOUT(n)   ((n) << 5)
+#define WDT_LENGTH      0x04
+#define WDT_LENGTH_KEY      0x8
+#define WDT_RST         0x08
+#define WDT_RST_RELOAD      0x1971
+#define WDT_NONRST_REG2     0x24
+#define WDT_STAGE_OFS       29
+#define WDT_STAGE_MASK      0x07
+#define WDT_STAGE_KERNEL    0x03
+#define CPU_NR (nr_cpu_ids)
+#define DEFAULT_INTERVAL    15
+#define WDT_COUNTER     0x514
+
+#define SYST0_CON		0x40
+#define SYST0_VAL		0x44
+
+#define SYSTIMER_CNTCV_L	(0x8)
+#define SYSTIMER_CNTCV_H	(0xC)
+
+/* bit 11 use set the reboot flag */
+#define REBOOT_FLAG_MASK 0x1
+#define REBOOT_FLAG_OFS 11
+
+/* Delay to change RGU timeout in ms */
+#define CHG_TMO_DLY_SEC		8L
+#define CHG_TMO_EN		0
+
+static int start_kicker(void);
+static int g_kicker_init;
+static DEFINE_SPINLOCK(lock);
+struct task_struct *wk_tsk[16] = { 0 };	/* max cpu 16 */
+static unsigned int wk_tsk_bind[16] = { 0 };	/* max cpu 16 */
+static unsigned long long wk_tsk_bind_time[16] = { 0 };	/* max cpu 16 */
+static unsigned long long wk_tsk_kick_time[16] = { 0 };	/* max cpu 16 */
+static char wk_tsk_buf[128] = { 0 };
+static unsigned long kick_bit;
+static int g_kinterval = -1;
+static struct work_struct wdk_work;
+static struct workqueue_struct *wdk_workqueue;
+static unsigned int lasthpg_act;
+static unsigned int lasthpg_cpu;
+static unsigned long long lasthpg_t;
+static unsigned long long wk_lasthpg_t[16] = { 0 };	/* max cpu 16 */
+static unsigned int cpuid_t[16] = { 0 };	/* max cpu 16 */
+static unsigned long long lastsuspend_t;
+static unsigned long long lastresume_t;
+static unsigned long long lastsuspend_syst;
+static unsigned long long lastresume_syst;
+static struct notifier_block wdt_pm_nb;
+static unsigned long g_nxtKickTime;
+static int g_hang_detected;
+static int g_change_tmo;
+static void __iomem *toprgu_base;
+static void __iomem *systimer_base;
+static unsigned int cpus_kick_bit;
+static atomic_t plug_mask = ATOMIC_INIT(0x0);
+static unsigned int cpus_skip_bit;
+
+static struct pt_regs saved_regs;
+struct timer_list aee_dump_timer;
+static unsigned long long aee_dump_timer_t;
+static unsigned long long all_k_timer_t;
+static unsigned int aee_dump_timer_c;
+static unsigned int cpus_skip_bit;
+
+static unsigned int get_check_bit(void)
+{
+	return cpus_kick_bit;
+}
+
+static unsigned int get_kick_bit(void)
+{
+	return kick_bit;
+}
+
+static int start_kicker_thread_with_default_setting(void)
+{
+	g_kinterval = DEFAULT_INTERVAL;
+	start_kicker();
+
+	pr_debug("[wdk] %s done\n", __func__);
+	return 0;
+}
+
+void wk_start_kick_cpu(int cpu)
+{
+	if (IS_ERR(wk_tsk[cpu])) {
+		pr_debug("[wdk] wk_task[%d] is NULL\n", cpu);
+	} else {
+		kthread_bind(wk_tsk[cpu], cpu);
+		pr_info("[wdk] bind thread %d to cpu %d\n",
+			wk_tsk[cpu]->pid, cpu);
+		wake_up_process(wk_tsk[cpu]);
+	}
+}
+
+void dump_wdk_bind_info(bool to_aee_sram)
+{
+	int i = 0;
+	int ret = 0;
+
+	ret = snprintf(wk_tsk_buf, sizeof(wk_tsk_buf),
+		"kick=0x%x,check=0x%x\n",
+		get_kick_bit(), get_check_bit());
+
+	if (ret >= 0)
+		pr_info("%s", wk_tsk_buf);
+#if IS_ENABLED(CONFIG_MTK_AEE_IPANIC)
+	if (to_aee_sram) {
+		aee_sram_fiq_log("\n");
+		aee_sram_fiq_log(wk_tsk_buf);
+	}
+#endif
+	for (i = 0; i < CPU_NR; i++) {
+		if (wk_tsk[i] != NULL) {
+			memset(wk_tsk_buf, 0, sizeof(wk_tsk_buf));
+			ret = snprintf(wk_tsk_buf, sizeof(wk_tsk_buf),
+				"[wdk]CPU %d, %d, %lld, %d, %ld, %lld\n",
+				i, wk_tsk_bind[i], wk_tsk_bind_time[i],
+				wk_tsk[i]->on_rq, wk_tsk[i]->state,
+				wk_tsk_kick_time[i]);
+
+			if (ret == 0)
+				return;
+#if IS_ENABLED(CONFIG_MTK_AEE_IPANIC)
+			if (to_aee_sram)
+				aee_sram_fiq_log(wk_tsk_buf);
+#endif
+			if (!to_aee_sram)
+				pr_info("%s", wk_tsk_buf);
+		}
+	}
+#if IS_ENABLED(CONFIG_MTK_AEE_IPANIC)
+	if (to_aee_sram)
+		aee_sram_fiq_log("\n");
+#endif
+}
+
+void kicker_cpu_bind(int cpu)
+{
+	if (IS_ERR(wk_tsk[cpu]))
+		pr_debug("[wdk]wk_task[%d] is NULL\n", cpu);
+	else {
+		/* kthread_bind(wk_tsk[cpu], cpu); */
+		WARN_ON_ONCE(set_cpus_allowed_ptr(wk_tsk[cpu],
+			cpumask_of(cpu)) < 0);
+		wake_up_process(wk_tsk[cpu]);
+		wk_tsk_bind[cpu] = 1;
+		wk_tsk_bind_time[cpu] = sched_clock();
+	}
+}
+
+void wk_cpu_update_bit_flag(unsigned int cpu, int plug_status, int set_check)
+{
+	if (plug_status == 1) {	/* plug on */
+		spin_lock(&lock);
+		if (set_check)
+			cpus_kick_bit |= (1 << cpu);
+		lasthpg_cpu = cpu;
+		lasthpg_act = plug_status;
+		lasthpg_t = sched_clock();
+		spin_unlock(&lock);
+	}
+	if (plug_status == 0) {	/* plug off */
+		spin_lock(&lock);
+		cpus_kick_bit &= (~(1 << cpu));
+		lasthpg_cpu = cpu;
+		lasthpg_act = plug_status;
+		lasthpg_t = sched_clock();
+		wk_tsk_bind[cpu] = 0;
+		spin_unlock(&lock);
+	}
+}
+
+extern int mrdump_common_die(int fiq_step, int reboot_reason, const char *msg,
+				  struct pt_regs *regs);
+
+static void kwdt_time_sync(void)
+{
+	struct rtc_time tm;
+	struct timespec64 tv = { 0 };
+	/* android time */
+	struct rtc_time tm_android;
+	struct timespec64 tv_android = { 0 };
+
+	ktime_get_real_ts64(&tv);
+	tv_android = tv;
+	rtc_time64_to_tm(tv.tv_sec, &tm);
+	tv_android.tv_sec -= (uint64_t)sys_tz.tz_minuteswest * 60;
+	rtc_time64_to_tm(tv_android.tv_sec, &tm_android);
+	pr_info("[thread:%d] %d-%02d-%02d %02d:%02d:%02d.%u UTC;"
+		"android time %d-%02d-%02d %02d:%02d:%02d.%03d\n",
+		current->pid, tm.tm_year + 1900, tm.tm_mon + 1,
+		tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+		(unsigned int)(tv.tv_nsec / 1000), tm_android.tm_year + 1900,
+		tm_android.tm_mon + 1, tm_android.tm_mday, tm_android.tm_hour,
+		tm_android.tm_min, tm_android.tm_sec,
+		(unsigned int)(tv_android.tv_nsec / 1000));
+}
+
+static void kwdt_dump_func(void)
+{
+	struct task_struct *g, *t;
+	int i = 0;
+
+	for_each_process_thread(g, t) {
+		if (!strcmp(t->comm, "watchdogd")) {
+#if IS_ENABLED(CONFIG_ARM64)
+			pr_info("watchdogd on CPU %d\n", t->cpu);
+#endif
+			sched_show_task(t);
+			break;
+		}
+	}
+
+	for (i = 0; i < CPU_NR; i++) {
+		struct rq *rq;
+
+		pr_info("task on CPU%d\n", i);
+		rq = cpu_rq(i);
+		if (cpu_rq(i))
+			sched_show_task(rq->curr);
+	}
+
+	dump_wdk_bind_info(false);
+
+	if (toprgu_base)
+		iowrite32(WDT_RST_RELOAD, toprgu_base + WDT_RST);
+	/* trigger HWT */
+	crash_setup_regs(&saved_regs, NULL);
+#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
+	mrdump_common_die(0, AEE_REBOOT_MODE_WDT, "HWT", &saved_regs);
+#endif
+}
+
+
+static void aee_dump_timer_func(struct timer_list *t)
+{
+	spin_lock(&lock);
+
+	if (sched_clock() - aee_dump_timer_t < CHG_TMO_DLY_SEC * 1000000000) {
+		g_change_tmo = 0;
+		aee_dump_timer_t = 0;
+		g_hang_detected = 0;
+		spin_unlock(&lock);
+		return;
+	}
+
+	if ((sched_clock() > all_k_timer_t) &&
+	    (sched_clock() - all_k_timer_t) < (CHG_TMO_DLY_SEC + 1) * 1000000000) {
+		g_change_tmo = 0;
+		aee_dump_timer_t = 0;
+		g_hang_detected = 0;
+		spin_unlock(&lock);
+		return;
+	} else if ((all_k_timer_t > sched_clock()) &&
+	    (ULLONG_MAX - all_k_timer_t + sched_clock()) < (CHG_TMO_DLY_SEC + 1) * 1000000000) {
+		g_change_tmo = 0;
+		aee_dump_timer_t = 0;
+		g_hang_detected = 0;
+		spin_unlock(&lock);
+		return;
+	}
+
+	if (!g_hang_detected ||
+	    (get_kick_bit() & get_check_bit()) == get_check_bit()) {
+		g_change_tmo = 0;
+		aee_dump_timer_t = 0;
+		spin_unlock(&lock);
+		if (toprgu_base) {
+			unsigned int tmo_len = 0;
+
+			tmo_len = ioread32(toprgu_base + WDT_LENGTH);
+			iowrite32(tmo_len | WDT_LENGTH_KEY, toprgu_base + WDT_LENGTH);
+			iowrite32(WDT_RST_RELOAD, toprgu_base + WDT_RST);
+		}
+	} else {
+		spin_unlock(&lock);
+		kwdt_dump_func();
+	}
+}
+
+static void kwdt_process_kick(int local_bit, int cpu,
+				unsigned long curInterval, char msg_buf[],
+				unsigned int original_kicker)
+{
+	unsigned int dump_timeout = 0, r_counter = DEFAULT_INTERVAL;
+	int i = 0, ret = -1;
+	bool rgu_fiq = false;
+
+	if (toprgu_base && (ioread32(toprgu_base + WDT_MODE) & WDT_MODE_EN))
+		r_counter = ioread32(toprgu_base + WDT_COUNTER) / (32 * 1024);
+
+	if (toprgu_base && (ioread32(toprgu_base + WDT_STATUS) & WDT_STATUS_IRQ))
+		rgu_fiq = true;
+
+	if (aee_dump_timer_t && ((sched_clock() - aee_dump_timer_t) >
+		(CHG_TMO_DLY_SEC + 5) * 1000000000)) {
+		if (!aee_dump_timer_c) {
+			aee_dump_timer_c = 1;
+			ret = snprintf(msg_buf, WK_MAX_MSG_SIZE, "wdtk-et %s %d cpu=%d o_k=%d\n",
+				  __func__, __LINE__, cpu, original_kicker);
+			spin_unlock(&lock);
+			if(ret != 0)
+				pr_info("%s", msg_buf);
+			kwdt_dump_func();
+			return;
+		}
+
+		ret = snprintf(msg_buf, WK_MAX_MSG_SIZE,
+			"all wdtk was already stopped cpu=%d o_k=%d\n",
+			cpu, original_kicker);
+
+		spin_unlock(&lock);
+		if(ret != 0)
+			pr_info("%s", msg_buf);
+		return;
+	}
+
+	local_bit = kick_bit;
+	if (cpu != original_kicker) {
+		/* wdtk-(original_kicker) is migrated to (cpu) */
+		local_bit |= (1 << original_kicker);
+	} else if ((local_bit & (1 << cpu)) == 0) {
+		/* pr_debug("[wdk] set kick_bit\n"); */
+		local_bit |= (1 << cpu);
+		/* aee_rr_rec_wdk_kick_jiffies(jiffies); */
+	} else if ((g_hang_detected == 0) &&
+			((local_bit & get_check_bit()) != get_check_bit()) &&
+			(sched_clock() - wk_lasthpg_t[cpu] >
+			 curInterval * 1000)) {
+		g_hang_detected = 1;
+		dump_timeout = 1;
+	}
+
+	if ((g_hang_detected == 0) &&
+			(r_counter < DEFAULT_INTERVAL - 10) && !g_change_tmo) {
+		g_hang_detected = 1;
+		dump_timeout = 2;
+	}
+
+	wk_tsk_kick_time[cpu] = sched_clock();
+	ret = snprintf(msg_buf, WK_MAX_MSG_SIZE,
+	 "[wdk-c] cpu=%d o_k=%d lbit=0x%x cbit=0x%x,%x,%d,%d,%lld,%x,%lld,%lld,%lld,%lld,[%lld,%ld] %d\n",
+	 cpu, original_kicker, local_bit, get_check_bit(),
+	 (local_bit ^ get_check_bit()) & get_check_bit(), lasthpg_cpu,
+	 lasthpg_act, lasthpg_t, atomic_read(&plug_mask), lastsuspend_t / 1000000,
+	 lastsuspend_syst / 1000000, lastresume_t / 1000000, lastresume_syst / 1000000,
+	 wk_tsk_kick_time[cpu], curInterval, r_counter);
+
+	if ((local_bit & get_check_bit()) == get_check_bit()) {
+		all_k_timer_t = sched_clock();
+		del_timer(&aee_dump_timer);
+		aee_dump_timer_t = 0;
+		cpus_skip_bit = 0;
+		msg_buf[5] = 'k';
+		g_hang_detected = 0;
+		dump_timeout = 0;
+		local_bit = 0;
+		kwdt_time_sync();
+		if (toprgu_base)
+			iowrite32(WDT_RST_RELOAD, toprgu_base + WDT_RST);
+	}
+
+	kick_bit = local_bit;
+
+	for (i = 0; i < CPU_NR; i++) {
+		if ((atomic_read(&plug_mask) & (1 << i)) || (i == cpu)) {
+			cpus_kick_bit |= (1 << i);
+			if (cpus_skip_bit & (1 << i))
+				cpus_kick_bit &= ~(1 << i);
+		}
+	}
+
+	if (cpu != original_kicker) {
+		cpus_kick_bit &= ~(1 << cpu);
+		cpus_skip_bit |= (1 << cpu);
+	}
+
+	spin_unlock(&lock);
+
+	if (ret != 0)
+		pr_info("%s", msg_buf);
+
+	if (dump_timeout) {
+		dump_wdk_bind_info(false);
+#if IS_ENABLED(CONFIG_MTK_IRQ_MONITOR)
+		if (p_mt_aee_dump_irq_info)
+			p_mt_aee_dump_irq_info();
+#endif
+
+		if (systimer_base)
+			pr_info("SYST0 CON%x VAL%x\n",
+				ioread32(systimer_base + SYST0_CON),
+				ioread32(systimer_base + SYST0_VAL));
+#if CHG_TMO_EN
+		if (toprgu_base) {
+			spin_lock_bh(&lock);
+			g_change_tmo = 1;
+			spin_unlock_bh(&lock);
+			iowrite32((WDT_LENGTH_TIMEOUT(6) << 6) | WDT_LENGTH_KEY,
+				toprgu_base + WDT_LENGTH);
+			iowrite32(WDT_RST_RELOAD, toprgu_base + WDT_RST);
+		}
+#endif
+		/* abort suspend when wdt kick */
+		if (dump_timeout == 2)
+			pm_system_wakeup();
+		else {
+			spin_lock_bh(&lock);
+			if (g_hang_detected && !aee_dump_timer_t) {
+				pm_system_wakeup();
+				aee_dump_timer_t = sched_clock();
+				g_change_tmo = 1;
+				spin_unlock_bh(&lock);
+				aee_dump_timer.expires = jiffies + CHG_TMO_DLY_SEC * HZ;
+				add_timer(&aee_dump_timer);
+				return;
+			}
+			spin_unlock_bh(&lock);
+		}
+	}
+
+	if (rgu_fiq)
+		pr_info("RGU IRQ triggered, but not raise FIQ\n");
+}
+
+static int kwdt_thread(void *arg)
+{
+	struct sched_param param = {.sched_priority = 99 };
+	int cpu = 0;
+	int local_bit = 0;
+	unsigned long curInterval = 0;
+	char msg_buf[WK_MAX_MSG_SIZE];
+
+	sched_setscheduler(current, SCHED_FIFO, &param);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	for (;;) {
+		if (kthread_should_stop()) {
+			pr_info("[wdk] kthread_should_stop do !!\n");
+			break;
+		}
+		msg_buf[0] = '\0';
+		/*
+		 * pr_debug("[wdk] loc_wk_wdt(%x),loc_wk_wdt->ready(%d)\n",
+		 * loc_wk_wdt ,loc_wk_wdt->ready);
+		 */
+		curInterval = g_kinterval*1000*1000;
+		spin_lock(&lock);
+		/* smp_processor_id does not
+		 * allowed preemptible context
+		 */
+		cpu = smp_processor_id();
+
+		/* to avoid wk_tsk[cpu] had not created out */
+		if (wk_tsk[cpu] != 0) {
+			if ((kick_bit & get_check_bit()) == 0) {
+				g_nxtKickTime = ktime_to_us(ktime_get())
+					+ g_kinterval*1000*1000;
+				curInterval = g_kinterval*1000*1000;
+			} else {
+				curInterval = g_nxtKickTime
+				- ktime_to_us(ktime_get());
+			}
+			/* to avoid interval too long */
+			if (curInterval > g_kinterval*1000*1000)
+				curInterval = g_kinterval*1000*1000;
+
+			kwdt_process_kick(local_bit, cpu, curInterval,
+				msg_buf, *((unsigned int *)arg));
+		} else {
+			spin_unlock(&lock);
+		}
+
+		usleep_range(curInterval, curInterval + SOFT_KICK_RANGE);
+	}
+	pr_debug("[wdk] wdk thread stop, cpu:%d, pid:%d\n", cpu, current->pid);
+	spin_unlock(&lock);
+	return 0;
+}
+
+static int start_kicker(void)
+{
+
+	int i;
+
+	for (i = 0; i < CPU_NR; i++) {
+		if (cpu_online(i)) {
+			cpuid_t[i] = i;
+			wk_tsk[i] = kthread_create(kwdt_thread,
+				(void *) &cpuid_t[i], "wdtk-%d", i);
+			if (IS_ERR(wk_tsk[i])) {
+				int ret = PTR_ERR(wk_tsk[i]);
+
+				wk_tsk[i] = NULL;
+				pr_info("[wdk]kthread_create failed, wdtk-%d\n", i);
+				return ret;
+			}
+			/* wk_cpu_update_bit_flag(i,1); */
+			wk_start_kick_cpu(i);
+			atomic_or(1 << i, &plug_mask);
+		} else
+			atomic_andnot(1 << i, &plug_mask);
+	}
+	g_kicker_init = 1;
+	pr_info("[wdk] WDT start kicker done CPU_NR=%d\n", CPU_NR);
+	return 0;
+}
+
+static int wk_cpu_callback_online(unsigned int cpu)
+{
+	wk_cpu_update_bit_flag(cpu, 1, 0);
+	wk_lasthpg_t[cpu] = sched_clock();
+	atomic_or(1 << cpu, &plug_mask);
+	/*
+	 * Bind WDK thread to this CPU.
+	 * NOTE: Thread binding must be executed after CPU is ready
+	 * (online).
+	 */
+	if (g_kicker_init == 1)
+		kicker_cpu_bind(cpu);
+	else
+		pr_info("kicker was not bound to CPU%d\n", cpu);
+
+	return 0;
+}
+
+static int wk_cpu_callback_offline(unsigned int cpu)
+{
+	wk_cpu_update_bit_flag(cpu, 0, 1);
+
+	atomic_andnot(1 << cpu, &plug_mask);
+	return 0;
+}
+
+static void wdk_work_callback(struct work_struct *work)
+{
+	int res = 0;
+	int i = 0;
+
+	cpu_hotplug_disable();
+
+	res = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+		"watchdog:wdkctrl:online", wk_cpu_callback_online, NULL);
+	if (res < 0)
+		pr_info("[wdk]setup CPUHP_AP_ONLINE_DYN fail %d\n", res);
+
+	res = cpuhp_setup_state_nocalls(CPUHP_BP_PREPARE_DYN,
+		"watchdog:wdkctrl:offline", NULL, wk_cpu_callback_offline);
+	if (res < 0)
+		pr_info("[wdk]setup CPUHP_BP_PREPARE_DYN fail %d\n", res);
+
+	for (i = 0; i < CPU_NR; i++) {
+		if (cpu_online(i)) {
+			wk_cpu_update_bit_flag(i, 1, 1);
+			pr_debug("[wdk]init cpu online %d\n", i);
+		} else {
+			wk_cpu_update_bit_flag(i, 0, 1);
+			pr_debug("[wdk]init cpu offline %d\n", i);
+		}
+	}
+
+	start_kicker_thread_with_default_setting();
+
+	cpu_hotplug_enable();
+
+	pr_info("[wdk]init_wk done late_initcall cpus_kick_bit=0x%x -----\n",
+		cpus_kick_bit);
+
+}
+
+static int wdt_pm_notify(struct notifier_block *notify_block,
+			unsigned long mode, void *unused)
+{
+	switch (mode) {
+	case PM_HIBERNATION_PREPARE:
+	case PM_SUSPEND_PREPARE:
+	case PM_RESTORE_PREPARE:
+		lastsuspend_t = sched_clock();
+		break;
+
+	case PM_POST_SUSPEND:
+	case PM_POST_HIBERNATION:
+	case PM_POST_RESTORE:
+		lastresume_t = sched_clock();
+		break;
+	}
+
+	return 0;
+}
+
+static int __init init_wk_check_bit(void)
+{
+	int i = 0;
+
+	pr_debug("[wdk]arch init check_bit=0x%x+++++\n", cpus_kick_bit);
+	for (i = 0; i < CPU_NR; i++)
+		wk_cpu_update_bit_flag(i, 1, 1);
+
+	pr_debug("[wdk]arch init check_bit=0x%x-----\n", cpus_kick_bit);
+	return 0;
+}
+
+static void reboot_set_flag(bool op)
+{
+	unsigned int reg = ioread32(toprgu_base + WDT_NONRST_REG2);
+
+	pr_info("reboot set flag, old value 0x%x, %d.\n", reg, op);
+	reg = ((reg & ~(REBOOT_FLAG_MASK << REBOOT_FLAG_OFS))
+		| (op ? 1 : 0) << REBOOT_FLAG_OFS);
+	iowrite32(reg, toprgu_base + WDT_NONRST_REG2);
+	pr_info("reboot set flag new value 0x%x.\n", reg);
+}
+
+static int aee_reset(struct notifier_block *nb, unsigned long action, void *data)
+{
+	reboot_set_flag(false);
+	return 0;
+}
+
+static struct notifier_block aee_reboot_notify = {
+	.notifier_call = aee_reset,
+};
+
+static void aee_reboot_hook_init(void)
+{
+	int ret = 0;
+
+	ret = register_reboot_notifier(&aee_reboot_notify);
+	if (ret)
+		pr_err("register restart handler failed: 0x%x.\n", ret);
+
+	/* set reboot flag */
+	reboot_set_flag(true);
+}
+
+static void aee_reboot_hook_exit(void)
+{
+	int ret = 0;
+
+	ret = unregister_reboot_notifier(&aee_reboot_notify);
+	if (ret != 0)
+		pr_err("unregister restart handler failed: 0x%x.\n", ret);
+
+	/* clear reboot flag */
+	reboot_set_flag(false);
+}
+
+static const struct of_device_id toprgu_of_match[] = {
+	{ .compatible = "mediatek,mt2735-wdt" },
+	{},
+};
+
+static const struct of_device_id systimer_of_match[] = {
+	{ .compatible = "mediatek,mt6765-timer" },
+	{},
+};
+
+static int __init hangdet_init(void)
+{
+	int res = 0;
+	struct device_node *np_toprgu, *np_systimer;
+
+	for_each_matching_node(np_toprgu, toprgu_of_match) {
+		pr_info("%s: compatible node found: %s\n",
+			 __func__, np_toprgu->name);
+		break;
+	}
+
+	toprgu_base = of_iomap(np_toprgu, 0);
+	if (!toprgu_base)
+		pr_debug("toprgu iomap failed\n");
+
+	for_each_matching_node(np_systimer, systimer_of_match) {
+		pr_info("%s: compatible node found: %s\n",
+			 __func__, np_systimer->name);
+		break;
+	}
+
+	systimer_base = of_iomap(np_systimer, 0);
+	if (!systimer_base)
+		pr_debug("systimer iomap failed\n");
+
+	init_wk_check_bit();
+
+	wdk_workqueue = create_singlethread_workqueue("mt-wdk");
+	INIT_WORK(&wdk_work, wdk_work_callback);
+
+	res = queue_work(wdk_workqueue, &wdk_work);
+
+	if (!res)
+		pr_info("[wdk]wdk_work start return:%d!\n", res);
+
+	wdt_pm_nb.notifier_call = wdt_pm_notify;
+	register_pm_notifier(&wdt_pm_nb);
+
+	if (systimer_base) {
+		uint64_t cnt;
+		uint32_t low;
+
+		low = readl(systimer_base + SYSTIMER_CNTCV_L);
+		cnt = readl(systimer_base + SYSTIMER_CNTCV_H);
+		cnt = cnt << 32 | low;
+
+		pr_info("%s systimer_cnt %lld\n", __func__, cnt);
+
+		cnt = sched_clock();
+
+		pr_info("%s set wdk_ktime %lld\n", __func__, cnt);
+	}
+
+	timer_setup(&aee_dump_timer, aee_dump_timer_func, 0);
+	aee_reboot_hook_init();
+
+	return 0;
+}
+
+static void __exit hangdet_exit(void)
+{
+	unregister_pm_notifier(&wdt_pm_nb);
+	kthread_stop((struct task_struct *)wk_tsk);
+	aee_reboot_hook_exit();
+}
+
+module_init(hangdet_init);
+module_exit(hangdet_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mediatek inc.");
+MODULE_DESCRIPTION("The cpu hang detector");
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/monitor_hang/monitor_hang.c b/src/kernel/linux/v4.19/drivers/misc/mediatek/monitor_hang/monitor_hang.c
index 625c152..9a1d44a 100644
--- a/src/kernel/linux/v4.19/drivers/misc/mediatek/monitor_hang/monitor_hang.c
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/monitor_hang/monitor_hang.c
@@ -30,6 +30,8 @@
 #include <asm/stacktrace.h>
 #include <asm/traps.h>
 #include <uapi/linux/sched/types.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 
 #if IS_ENABLED(CONFIG_MTK_BOOT)
 #include <mt-plat/mtk_boot_common.h>
@@ -44,6 +46,8 @@
 #include "monitor_hang.h"
 #include "mrdump.h"
 
+//#define BOOT_UP_HANG
+
 #ifndef TASK_STATE_TO_CHAR_STR
 #define TASK_STATE_TO_CHAR_STR "RSDTtXZxKWPNn"
 #endif
@@ -1012,7 +1016,14 @@
 			!strcmp(p->comm, "vdc") ||
 			!strcmp(p->comm, "debuggerd") ||
 			/* dump colgin process */
-			!strcmp(p->comm, "procd")) {
+			!strcmp(p->comm, "procd") ||
+			/* dump yocto process */
+			!strcmp(p->comm, "systemd") ||
+			!strcmp(p->comm, "systemd-journal") ||
+			!strcmp(p->comm, "systemd-udevd") ||
+			!strcmp(p->comm, "systemd-logind") ||
+			!strcmp(p->comm, "systemd-shutdow") ||
+			!strcmp(p->comm, "reboot")) {
 			rcu_read_unlock();
 			show_bt_by_pid(p->pid);
 			rcu_read_lock();
@@ -1142,12 +1153,12 @@
 
 	sched_setscheduler(current, SCHED_FIFO, &param);
 	reset_hang_info();
-	msleep(120 * 1000);
+	msleep(60 * 1000);
 	pr_debug("[Hang_Detect] hang_detect thread starts.\n");
 
 #ifdef BOOT_UP_HANG
-	hd_timeout = 9;
-	hang_detect_counter = 9;
+	hd_timeout = 3;
+	hang_detect_counter = 3;
 	hd_detect_enabled = true;
 #endif
 
@@ -1162,10 +1173,6 @@
 		{
 
 			if (hang_detect_counter <= 0) {
-				log_hang_info(
-					"[Hang_detect]Dump the %d time process bt.\n",
-					Hang_Detect_first ? 2 : 1);
-
 				if(Hang_Detect_first == false) {
 #ifdef CONFIG_MTK_HANG_DETECT_DB
 					memset(Hang_Info, 0, MaxHangInfoSize);
@@ -1173,6 +1180,10 @@
 #endif
 				}
 
+				log_hang_info(
+					"[Hang_detect]Dump the %d time process bt.\n",
+					Hang_Detect_first ? 2 : 1);
+
 				if (Hang_Detect_first == true
 					&& dump_bt_done != 1) {
 		/* some time dump thread will block in dumping native bt */
@@ -1264,10 +1275,59 @@
 }
 
 extern int __init aee_parse_chosen(void);
+static int mt_hang_detect_proc_show(struct seq_file *m, void *v)
+{
+	seq_puts(m, "=== hang_detect monitor ===\n");
+	seq_printf(m, "hang_detect_enabled: %d\n", hd_detect_enabled);
+	seq_printf(m, "hd_timeout: %d\n", hd_timeout);
+	return 0;
+}
+
+static int mt_hang_detect_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, mt_hang_detect_proc_show, inode->i_private);
+}
+
+static ssize_t mt_hang_detect_proc_write(struct file *filp,
+	const char *ubuf, size_t cnt, loff_t *data)
+{
+	char buf[64];
+	int val;
+	int ret;
+
+	if (cnt >= sizeof(buf))
+		return -EINVAL;
+
+	if (copy_from_user(&buf, ubuf, cnt))
+		return -EFAULT;
+
+	buf[cnt] = 0;
+
+	ret = kstrtoint(buf, 10, &val);
+	if (ret < 0)
+		return ret;
+
+	if (val < 0 || val > 300) {
+		return -EINVAL;
+	} else {
+		monitor_hang_kick(val);
+	}
+
+	return cnt;
+}
+
+static const struct file_operations mt_hang_detect_proc_fops = {
+	.open = mt_hang_detect_proc_open,
+	.write = mt_hang_detect_proc_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
 
 static int __init monitor_hang_init(void)
 {
 	int err = 0;
+	struct proc_dir_entry *pe;
 
 	if (!aee_parse_chosen())
 		return err;
@@ -1283,6 +1343,12 @@
 		pr_notice("failed to register Hang_Monitor_dev device!\n");
 		return err;
 	}
+
+	pe = proc_create("hang_detect_monitor", 0664, NULL, &mt_hang_detect_proc_fops);
+	if (!pe) {
+		pr_err("failed to create /proc/hang_detect_monitor file!\n");
+		return -ENOMEM;
+	}
 	hang_detect_init();
 	return err;
 }
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/monitor_hang/monitor_hang.h b/src/kernel/linux/v4.19/drivers/misc/mediatek/monitor_hang/monitor_hang.h
index f0f4416..f2b570d 100644
--- a/src/kernel/linux/v4.19/drivers/misc/mediatek/monitor_hang/monitor_hang.h
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/monitor_hang/monitor_hang.h
@@ -7,7 +7,7 @@
 #define __MONITOR_HANG_H__
 
 #define HD_PROC "hang_detect"
-#define HD_INTER 30	/* 1 tick is 30 seconds*/
+#define HD_INTER 10	/* 1 tick is 10 seconds*/
 
 struct name_list {
 	char name[TASK_COMM_LEN + 1];
diff --git a/src/kernel/linux/v4.19/drivers/net/wireless/bcmdhd/dhd_linux.c b/src/kernel/linux/v4.19/drivers/net/wireless/bcmdhd/dhd_linux.c
index 9164ea7..2aff5ed 100755
--- a/src/kernel/linux/v4.19/drivers/net/wireless/bcmdhd/dhd_linux.c
+++ b/src/kernel/linux/v4.19/drivers/net/wireless/bcmdhd/dhd_linux.c
@@ -8776,6 +8776,7 @@
 	dhd_bus_get_ids(bus, &bus_type, &bus_num, &slot_num);
 #endif // endif
 	adapter = dhd_wifi_platform_get_adapter(bus_type, bus_num, slot_num);
+	DHD_ERROR(("%s: dhd_wifi_platform_get_adapter completed!\n", __FUNCTION__));
 
 	/* Allocate primary dhd_info */
 	dhd = wifi_platform_prealloc(adapter, DHD_PREALLOC_DHD_INFO, sizeof(dhd_info_t));
@@ -8786,6 +8787,8 @@
 			goto dhd_null_flag;
 		}
 	}
+	DHD_ERROR(("%s: wifi_platform_prealloc completed!\n", __FUNCTION__));
+	
 	memset(dhd, 0, sizeof(dhd_info_t));
 	dhd_state |= DHD_ATTACH_STATE_DHD_ALLOC;
 
@@ -8835,7 +8838,10 @@
 	dhd->pub.wet_info = dhd_get_wet_info(&dhd->pub);
 #endif /* DHD_WET */
 	/* Initialize thread based operation and lock */
+	DHD_ERROR(("%s: sema_init enter!\n", __FUNCTION__));
 	sema_init(&dhd->sdsem, 1);
+	DHD_ERROR(("%s: sema_init completed!\n", __FUNCTION__));
+
 
 	/* Some DHD modules (e.g. cfg80211) configures operation mode based on firmware name.
 	 * This is indeed a hack but we have to make it work properly before we have a better
@@ -8891,6 +8897,7 @@
 
 #ifdef PROP_TXSTATUS
 	spin_lock_init(&dhd->wlfc_spinlock);
+	DHD_ERROR(("%s: spin_lock_init:dhd->wlfc_spinlock completed!\n", __FUNCTION__));
 
 	dhd->pub.skip_fc = dhd_wlfc_skip_fc;
 	dhd->pub.plat_init = dhd_wlfc_plat_init;
@@ -8905,6 +8912,7 @@
 	} else {
 		wake_up_process(dhd->pub.wlfc_thread);
 	}
+	DHD_ERROR(("%s: kthread_create: dhd_wlfc_transfer_packets completed!\n", __FUNCTION__));
 #endif /* DHD_WLFC_THREAD */
 #endif /* PROP_TXSTATUS */
 
@@ -8917,6 +8925,7 @@
 	init_waitqueue_head(&dhd->pub.tx_completion_wait);
 	dhd->pub.dhd_bus_busy_state = 0;
 	/* Initialize the spinlocks */
+	DHD_ERROR(("%s: Initialize the spinlocks enter!\n", __FUNCTION__));
 	spin_lock_init(&dhd->sdlock);
 	spin_lock_init(&dhd->txqlock);
 	spin_lock_init(&dhd->dhd_lock);
@@ -8960,17 +8969,21 @@
 
 #ifdef WL_CFG80211
 	spin_lock_init(&dhd->pub.up_lock);
+	DHD_ERROR(("%s: Initialize the spinlocks completed!\n", __FUNCTION__));
+
 	/* Attach and link in the cfg80211 */
 	if (unlikely(wl_cfg80211_attach(net, &dhd->pub))) {
 		DHD_ERROR(("wl_cfg80211_attach failed\n"));
 		goto fail;
 	}
+	DHD_ERROR(("%s: wl_cfg80211_attach completed!\n", __FUNCTION__));
 
 #ifdef DHD_MONITOR_INTERFACE
 	dhd_monitor_init(&dhd->pub);
 #endif /* DHD_MONITOR_INTERFACE */
 	dhd_state |= DHD_ATTACH_STATE_CFG80211;
 #endif /* WL_CFG80211 */
+	DHD_ERROR(("%s: dhd_monitor_init completed!\n", __FUNCTION__));
 
 #if defined(WL_WIRELESS_EXT)
 	/* Attach and link in the iw */
@@ -9040,6 +9053,7 @@
 		DHD_ERROR(("%s: Initializing %u sta\n", __FUNCTION__, DHD_MAX_STA));
 		goto fail;
 	}
+	DHD_ERROR(("%s: dhd_sta_pool_init completed!\n", __FUNCTION__));
 
 #ifdef DHD_PCIE_NATIVE_RUNTIMEPM
 	dhd->tx_wq = alloc_workqueue("bcmdhd-tx-wq", WQ_HIGHPRI | WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
@@ -9070,6 +9084,7 @@
 	} else {
 		dhd->thr_wdt_ctl.thr_pid = -1;
 	}
+	DHD_ERROR(("%s: PROC_START: dhd_watchdog_thread completed!\n", __FUNCTION__));
 
 #ifdef DHD_PCIE_RUNTIMEPM
 	/* Setup up the runtime PM Idlecount timer */
@@ -9079,6 +9094,7 @@
 	dhd->thr_rpm_ctl.thr_pid = DHD_PID_KT_INVALID;
 	PROC_START(dhd_rpm_state_thread, dhd, &dhd->thr_rpm_ctl, 0, "dhd_rpm_state_thread");
 	if (dhd->thr_rpm_ctl.thr_pid < 0) {
+		DHD_ERROR(("%s: PROC_START: dhd_rpm_state_thread failed!\n", __FUNCTION__));
 		goto fail;
 	}
 #endif /* DHD_PCIE_RUNTIMEPM */
@@ -9092,6 +9108,7 @@
 		/* Initialize DPC thread */
 		PROC_START(dhd_dpc_thread, dhd, &dhd->thr_dpc_ctl, 0, "dhd_dpc");
 		if (dhd->thr_dpc_ctl.thr_pid < 0) {
+			DHD_ERROR(("%s: PROC_START: dhd_dpc failed!\n", __FUNCTION__));
 			goto fail;
 		}
 	} else {
@@ -9105,6 +9122,7 @@
 		/* Initialize RXF thread */
 		PROC_START(dhd_rxf_thread, dhd, &dhd->thr_rxf_ctl, 0, "dhd_rxf");
 		if (dhd->thr_rxf_ctl.thr_pid < 0) {
+			DHD_ERROR(("%s: PROC_START: dhd_rxf failed!\n", __FUNCTION__));
 			goto fail;
 		}
 	}
@@ -9143,6 +9161,7 @@
 		register_inet6addr_notifier(&dhd_inet6addr_notifier);
 	}
 #endif /* CONFIG_IPV6 && IPV6_NDO_SUPPORT */
+	DHD_ERROR(("%s: dhd_deferred_work_init enter!\n", __FUNCTION__));
 	dhd->dhd_deferred_wq = dhd_deferred_work_init((void *)dhd);
 #if defined(OEM_ANDROID)
 	INIT_WORK(&dhd->dhd_hang_process_work, dhd_hang_process);
diff --git a/src/kernel/linux/v4.19/drivers/net/wireless/bcmdhd/dhd_linux_wq.c b/src/kernel/linux/v4.19/drivers/net/wireless/bcmdhd/dhd_linux_wq.c
index 54de6a7..9c50ffd 100644
--- a/src/kernel/linux/v4.19/drivers/net/wireless/bcmdhd/dhd_linux_wq.c
+++ b/src/kernel/linux/v4.19/drivers/net/wireless/bcmdhd/dhd_linux_wq.c
@@ -129,6 +129,7 @@
 
 	/* initialize event fifo */
 	spin_lock_init(&work->work_lock);
+	DHD_ERROR(("%s: spin_lock_init completed!\n", __FUNCTION__));
 
 	/* allocate buffer to hold prio events */
 	fifo_size = DHD_PRIO_WORK_FIFO_SIZE;
@@ -145,6 +146,8 @@
 	work->prio_fifo = dhd_kfifo_init(buf, fifo_size, &work->work_lock);
 	if (!work->prio_fifo) {
 		kfree(buf);
+		DHD_ERROR(("%s: dhd_kfifo_init: prio_fifo failed\n",
+			__FUNCTION__));
 		goto return_null;
 	}
 
@@ -162,6 +165,8 @@
 	work->work_fifo = dhd_kfifo_init(buf, fifo_size, &work->work_lock);
 	if (!work->work_fifo) {
 		kfree(buf);
+		DHD_ERROR(("%s: dhd_kfifo_init: work_fifo failed\n",
+			__FUNCTION__));
 		goto return_null;
 	}