216 lines
5.1 KiB
C
216 lines
5.1 KiB
C
/* Copyright (c) 2012-2013, Linux Foundation. All rights reserved.
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only 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 the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/completion.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/module.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/preempt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/cpumask.h>
|
|
#include <asm/barrier.h>
|
|
#include <asm/io.h>
|
|
#include <asm-generic/sizes.h>
|
|
#include <mach/scm.h>
|
|
|
|
#define REG_MPM2_WDOG_BASE 0xFC4AA000
|
|
#define REG_OFFSET_MPM2_WDOG_RESET 0x0
|
|
#define REG_OFFSET_MPM2_WDOG_BITE_VAL 0x10
|
|
|
|
#define REG_VAL_WDOG_RESET_DO_RESET 0x1
|
|
#define REG_VAL_WDOG_BITE_VAL 0x400
|
|
|
|
#define SCM_SVC_SEC_WDOG_TRIG 0x8
|
|
|
|
#define MDELAY_TIME 15000
|
|
|
|
static int apps_wdog_bite;
|
|
static int sec_wdog_bite;
|
|
static int sec_wdog_scm;
|
|
static int apps_wdog_bark;
|
|
|
|
static int apps_wdog_bite_set(const char *val, struct kernel_param *kp);
|
|
module_param_call(apps_wdog_bite, apps_wdog_bite_set, param_get_int,
|
|
&apps_wdog_bite, 0644);
|
|
|
|
static int sec_wdog_bite_set(const char *val, struct kernel_param *kp);
|
|
module_param_call(sec_wdog_bite, sec_wdog_bite_set, param_get_int,
|
|
&sec_wdog_bite, 0644);
|
|
|
|
static int sec_wdog_scm_set(const char *val, struct kernel_param *kp);
|
|
module_param_call(sec_wdog_scm, sec_wdog_scm_set, param_get_int,
|
|
&sec_wdog_scm, 0644);
|
|
|
|
static int apps_wdog_bark_set(const char *val, struct kernel_param *kp);
|
|
module_param_call(apps_wdog_bark, apps_wdog_bark_set, param_get_int,
|
|
&apps_wdog_bark, 0644);
|
|
|
|
struct completion timeout_complete;
|
|
|
|
static void timeout_work(struct work_struct *work)
|
|
{
|
|
pr_info("apps watchdog bark\n");
|
|
preempt_disable();
|
|
mdelay(MDELAY_TIME);
|
|
preempt_enable();
|
|
complete(&timeout_complete);
|
|
}
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
static void bring_other_cpus_down(void)
|
|
{
|
|
int cpu;
|
|
|
|
for_each_online_cpu(cpu) {
|
|
if (cpu == 0)
|
|
continue;
|
|
cpu_down(cpu);
|
|
}
|
|
}
|
|
#else
|
|
static void bring_other_cpus_down(void)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static void apps_bite_work(struct work_struct *work)
|
|
{
|
|
bring_other_cpus_down();
|
|
pr_info("apps watchdog bite\n");
|
|
local_irq_disable();
|
|
mdelay(MDELAY_TIME);
|
|
local_irq_enable();
|
|
pr_err("apps watchdog bite failed\n");
|
|
complete(&timeout_complete);
|
|
}
|
|
|
|
static void sec_wdog_bite_work(struct work_struct *work)
|
|
{
|
|
static void *sec_wdog_virt;
|
|
bring_other_cpus_down();
|
|
sec_wdog_virt = ioremap(REG_MPM2_WDOG_BASE, SZ_4K);
|
|
if (!sec_wdog_virt) {
|
|
pr_info("unable to map sec wdog page\n");
|
|
goto err;
|
|
}
|
|
writel_relaxed(REG_VAL_WDOG_RESET_DO_RESET,
|
|
sec_wdog_virt + REG_OFFSET_MPM2_WDOG_RESET);
|
|
writel_relaxed(REG_VAL_WDOG_BITE_VAL,
|
|
sec_wdog_virt + REG_OFFSET_MPM2_WDOG_BITE_VAL);
|
|
mb();
|
|
mdelay(MDELAY_TIME);
|
|
err:
|
|
complete(&timeout_complete);
|
|
}
|
|
|
|
/*
|
|
* If this returns operation failed
|
|
*/
|
|
static DECLARE_WORK(apps_bite_work_struct, apps_bite_work);
|
|
static int apps_wdog_bite_set(const char *val, struct kernel_param *kp)
|
|
{
|
|
int ret;
|
|
int old_val;
|
|
|
|
old_val = apps_wdog_bite;
|
|
ret = param_set_int(val, kp);
|
|
if (ret)
|
|
return ret;
|
|
if (apps_wdog_bite == 1) {
|
|
init_completion(&timeout_complete);
|
|
schedule_work_on(0, &apps_bite_work_struct);
|
|
wait_for_completion(&timeout_complete);
|
|
}
|
|
return -EIO;
|
|
}
|
|
|
|
/*
|
|
* If this returns operation failed
|
|
*/
|
|
static DECLARE_WORK(sec_wdog_bite_work_struct, sec_wdog_bite_work);
|
|
static int sec_wdog_bite_set(const char *val, struct kernel_param *kp)
|
|
{
|
|
int ret;
|
|
int old_val;
|
|
|
|
old_val = sec_wdog_bite;
|
|
ret = param_set_int(val, kp);
|
|
if (ret)
|
|
return ret;
|
|
if (sec_wdog_bite == 1) {
|
|
init_completion(&timeout_complete);
|
|
schedule_work_on(0, &sec_wdog_bite_work_struct);
|
|
wait_for_completion(&timeout_complete);
|
|
}
|
|
return -EIO;
|
|
}
|
|
|
|
/*
|
|
* If this returns operation failed
|
|
*/
|
|
static int sec_wdog_scm_set(const char *val, struct kernel_param *kp)
|
|
{
|
|
int ret;
|
|
int old_val;
|
|
|
|
old_val = sec_wdog_scm;
|
|
ret = param_set_int(val, kp);
|
|
if (ret)
|
|
return ret;
|
|
if (sec_wdog_scm == 1) {
|
|
u8 trigger = 0;
|
|
pr_info("sec watchdog bite\n");
|
|
ret = scm_call(SCM_SVC_BOOT, SCM_SVC_SEC_WDOG_TRIG, &trigger,
|
|
sizeof(trigger), NULL, 0);
|
|
pr_err("Secure watchdog bite failed\n");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* If this returns operation failed
|
|
*/
|
|
static DECLARE_WORK(timeout_work_struct, timeout_work);
|
|
static int apps_wdog_bark_set(const char *val, struct kernel_param *kp)
|
|
{
|
|
int ret;
|
|
int old_val;
|
|
|
|
old_val = apps_wdog_bark;
|
|
ret = param_set_int(val, kp);
|
|
if (ret)
|
|
return ret;
|
|
if (apps_wdog_bark == 1) {
|
|
init_completion(&timeout_complete);
|
|
schedule_work_on(0, &timeout_work_struct);
|
|
wait_for_completion(&timeout_complete);
|
|
pr_err("Failed to trigger apps bark\n");
|
|
}
|
|
return -EIO;
|
|
}
|
|
|
|
static int msm_watchdog_test_init(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void msm_watchdog_test_exit(void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
module_init(msm_watchdog_test_init);
|
|
module_exit(msm_watchdog_test_exit);
|
|
MODULE_LICENSE("GPL v2");
|