[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, ¶m);
+ 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, ¶m);
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;
}