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");
 | 
