mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
msm: Allow watchdog disable via sysfs
To debug crashes allow watchdog to be disabled from userspace via sysfs. Signed-off-by: Hanumant Singh <hanumant@codeaurora.org> (cherry picked from commit 42ffa44fe6ac67390037ff95be4a16c0ecf145c0) Conflicts: arch/arm/mach-msm/msm_watchdog_v2.c Change-Id: Ia2db8dd6262c9bc518ae736c479ae042d01345b5 Signed-off-by: Sudhir Sharma <sudsha@codeaurora.org>
This commit is contained in:
parent
b873ee8106
commit
6707c2492b
1 changed files with 122 additions and 34 deletions
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
|
|
||||||
#define MASK_SIZE 32
|
#define MASK_SIZE 32
|
||||||
#define SCM_SET_REGSAVE_CMD 0x2
|
#define SCM_SET_REGSAVE_CMD 0x2
|
||||||
|
#define SCM_SVC_SEC_WDOG_DIS 0x7
|
||||||
|
|
||||||
struct msm_watchdog_data {
|
struct msm_watchdog_data {
|
||||||
unsigned int __iomem phys_base;
|
unsigned int __iomem phys_base;
|
||||||
|
@ -52,6 +54,7 @@ struct msm_watchdog_data {
|
||||||
unsigned long long min_slack_ns;
|
unsigned long long min_slack_ns;
|
||||||
void *scm_regsave;
|
void *scm_regsave;
|
||||||
cpumask_t alive_mask;
|
cpumask_t alive_mask;
|
||||||
|
struct mutex disable_lock;
|
||||||
struct work_struct init_dogwork_struct;
|
struct work_struct init_dogwork_struct;
|
||||||
struct delayed_work dogwork_struct;
|
struct delayed_work dogwork_struct;
|
||||||
struct notifier_block panic_blk;
|
struct notifier_block panic_blk;
|
||||||
|
@ -73,19 +76,6 @@ module_param(enable, int, 0);
|
||||||
static long WDT_HZ = 32765;
|
static long WDT_HZ = 32765;
|
||||||
module_param(WDT_HZ, long, 0);
|
module_param(WDT_HZ, long, 0);
|
||||||
|
|
||||||
/*
|
|
||||||
* If the watchdog is enabled at bootup (enable=1),
|
|
||||||
* the runtime_disable sysfs node at
|
|
||||||
* /sys/module/msm_watchdog/parameters/runtime_disable
|
|
||||||
* can be used to deactivate the watchdog.
|
|
||||||
* This is a one-time setting. The watchdog
|
|
||||||
* cannot be re-enabled once it is disabled.
|
|
||||||
*/
|
|
||||||
static int runtime_disable;
|
|
||||||
static int wdog_enable_set(const char *val, struct kernel_param *kp);
|
|
||||||
module_param_call(runtime_disable, wdog_enable_set, param_get_int,
|
|
||||||
&runtime_disable, 0644);
|
|
||||||
|
|
||||||
static void pet_watchdog_work(struct work_struct *work);
|
static void pet_watchdog_work(struct work_struct *work);
|
||||||
static void init_watchdog_work(struct work_struct *work);
|
static void init_watchdog_work(struct work_struct *work);
|
||||||
|
|
||||||
|
@ -138,14 +128,99 @@ static int panic_wdog_handler(struct notifier_block *this,
|
||||||
}
|
}
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* TODO: implement enable/disable.
|
static void wdog_disable(struct msm_watchdog_data *wdog_dd)
|
||||||
*/
|
|
||||||
static int wdog_enable_set(const char *val, struct kernel_param *kp)
|
|
||||||
{
|
{
|
||||||
return 0;
|
__raw_writel(0, wdog_dd->base + WDT0_EN);
|
||||||
|
mb();
|
||||||
|
if (wdog_dd->irq_ppi) {
|
||||||
|
disable_percpu_irq(wdog_dd->bark_irq);
|
||||||
|
free_percpu_irq(wdog_dd->bark_irq, wdog_dd->wdog_cpu_dd);
|
||||||
|
} else
|
||||||
|
devm_free_irq(wdog_dd->dev, wdog_dd->bark_irq, wdog_dd);
|
||||||
|
enable = 0;
|
||||||
|
/*Ensure all cpus see update to enable*/
|
||||||
|
smp_mb();
|
||||||
|
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||||
|
&wdog_dd->panic_blk);
|
||||||
|
cancel_delayed_work_sync(&wdog_dd->dogwork_struct);
|
||||||
|
/* may be suspended after the first write above */
|
||||||
|
__raw_writel(0, wdog_dd->base + WDT0_EN);
|
||||||
|
mb();
|
||||||
|
pr_info("MSM Apps Watchdog deactivated.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wdog_disable_work_data {
|
||||||
|
struct work_struct work;
|
||||||
|
struct completion complete;
|
||||||
|
struct msm_watchdog_data *wdog_dd;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void wdog_disable_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct wdog_disable_work_data *work_data =
|
||||||
|
container_of(work, struct wdog_disable_work_data, work);
|
||||||
|
wdog_disable(work_data->wdog_dd);
|
||||||
|
complete(&work_data->complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t wdog_disable_get(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct msm_watchdog_data *wdog_dd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
mutex_lock(&wdog_dd->disable_lock);
|
||||||
|
ret = snprintf(buf, PAGE_SIZE, "%d\n", enable == 0 ? 1 : 0);
|
||||||
|
mutex_unlock(&wdog_dd->disable_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t wdog_disable_set(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 disable;
|
||||||
|
struct wdog_disable_work_data work_data;
|
||||||
|
struct msm_watchdog_data *wdog_dd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
ret = kstrtou8(buf, 10, &disable);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(wdog_dd->dev, "invalid user input\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (disable == 1) {
|
||||||
|
mutex_lock(&wdog_dd->disable_lock);
|
||||||
|
if (enable == 0) {
|
||||||
|
pr_info("MSM Apps Watchdog already disabled\n");
|
||||||
|
mutex_unlock(&wdog_dd->disable_lock);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
disable = 1;
|
||||||
|
ret = scm_call(SCM_SVC_BOOT, SCM_SVC_SEC_WDOG_DIS, &disable,
|
||||||
|
sizeof(disable), NULL, 0);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(wdog_dd->dev,
|
||||||
|
"Failed to deactivate secure wdog\n");
|
||||||
|
mutex_unlock(&wdog_dd->disable_lock);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
work_data.wdog_dd = wdog_dd;
|
||||||
|
init_completion(&work_data.complete);
|
||||||
|
INIT_WORK_ONSTACK(&work_data.work, wdog_disable_work);
|
||||||
|
schedule_work_on(0, &work_data.work);
|
||||||
|
wait_for_completion(&work_data.complete);
|
||||||
|
mutex_unlock(&wdog_dd->disable_lock);
|
||||||
|
} else {
|
||||||
|
pr_err("invalid operation, only disable = 1 supported\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(disable, S_IWUSR | S_IRUSR, wdog_disable_get,
|
||||||
|
wdog_disable_set);
|
||||||
|
|
||||||
static void pet_watchdog(struct msm_watchdog_data *wdog_dd)
|
static void pet_watchdog(struct msm_watchdog_data *wdog_dd)
|
||||||
{
|
{
|
||||||
|
@ -195,9 +270,13 @@ static void pet_watchdog_work(struct work_struct *work)
|
||||||
struct msm_watchdog_data,
|
struct msm_watchdog_data,
|
||||||
dogwork_struct);
|
dogwork_struct);
|
||||||
delay_time = msecs_to_jiffies(wdog_dd->pet_time);
|
delay_time = msecs_to_jiffies(wdog_dd->pet_time);
|
||||||
if (wdog_dd->do_ipi_ping)
|
if (enable) {
|
||||||
ping_other_cpus(wdog_dd);
|
if (wdog_dd->do_ipi_ping)
|
||||||
pet_watchdog(wdog_dd);
|
ping_other_cpus(wdog_dd);
|
||||||
|
pet_watchdog(wdog_dd);
|
||||||
|
}
|
||||||
|
/* Check again before scheduling *
|
||||||
|
* Could have been changed on other cpu */
|
||||||
if (enable)
|
if (enable)
|
||||||
schedule_delayed_work(&wdog_dd->dogwork_struct,
|
schedule_delayed_work(&wdog_dd->dogwork_struct,
|
||||||
delay_time);
|
delay_time);
|
||||||
|
@ -205,21 +284,24 @@ static void pet_watchdog_work(struct work_struct *work)
|
||||||
|
|
||||||
static int msm_watchdog_remove(struct platform_device *pdev)
|
static int msm_watchdog_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct wdog_disable_work_data work_data;
|
||||||
struct msm_watchdog_data *wdog_dd =
|
struct msm_watchdog_data *wdog_dd =
|
||||||
(struct msm_watchdog_data *)platform_get_drvdata(pdev);
|
(struct msm_watchdog_data *)platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
mutex_lock(&wdog_dd->disable_lock);
|
||||||
if (enable) {
|
if (enable) {
|
||||||
__raw_writel(0, wdog_dd->base + WDT0_EN);
|
work_data.wdog_dd = wdog_dd;
|
||||||
mb();
|
init_completion(&work_data.complete);
|
||||||
enable = 0;
|
INIT_WORK_ONSTACK(&work_data.work, wdog_disable_work);
|
||||||
/*
|
schedule_work_on(0, &work_data.work);
|
||||||
* TODO: Not sure if we need to call into TZ to disable
|
wait_for_completion(&work_data.complete);
|
||||||
* secure wdog.
|
|
||||||
*/
|
|
||||||
/* In case we got suspended mid-exit */
|
|
||||||
__raw_writel(0, wdog_dd->base + WDT0_EN);
|
|
||||||
}
|
}
|
||||||
|
mutex_unlock(&wdog_dd->disable_lock);
|
||||||
|
device_remove_file(wdog_dd->dev, &dev_attr_disable);
|
||||||
|
if (wdog_dd->irq_ppi)
|
||||||
|
free_percpu(wdog_dd->wdog_cpu_dd);
|
||||||
printk(KERN_INFO "MSM Watchdog Exit - Deactivated\n");
|
printk(KERN_INFO "MSM Watchdog Exit - Deactivated\n");
|
||||||
kzfree(wdog_dd);
|
kfree(wdog_dd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,6 +368,7 @@ static void init_watchdog_work(struct work_struct *work)
|
||||||
struct msm_watchdog_data,
|
struct msm_watchdog_data,
|
||||||
init_dogwork_struct);
|
init_dogwork_struct);
|
||||||
unsigned long delay_time;
|
unsigned long delay_time;
|
||||||
|
int error;
|
||||||
u64 timeout;
|
u64 timeout;
|
||||||
delay_time = msecs_to_jiffies(wdog_dd->pet_time);
|
delay_time = msecs_to_jiffies(wdog_dd->pet_time);
|
||||||
wdog_dd->min_slack_ticks = UINT_MAX;
|
wdog_dd->min_slack_ticks = UINT_MAX;
|
||||||
|
@ -298,12 +381,17 @@ static void init_watchdog_work(struct work_struct *work)
|
||||||
wdog_dd->panic_blk.notifier_call = panic_wdog_handler;
|
wdog_dd->panic_blk.notifier_call = panic_wdog_handler;
|
||||||
atomic_notifier_chain_register(&panic_notifier_list,
|
atomic_notifier_chain_register(&panic_notifier_list,
|
||||||
&wdog_dd->panic_blk);
|
&wdog_dd->panic_blk);
|
||||||
schedule_delayed_work(&wdog_dd->dogwork_struct, delay_time);
|
mutex_init(&wdog_dd->disable_lock);
|
||||||
|
schedule_delayed_work_on(0, &wdog_dd->dogwork_struct, delay_time);
|
||||||
__raw_writel(1, wdog_dd->base + WDT0_EN);
|
__raw_writel(1, wdog_dd->base + WDT0_EN);
|
||||||
__raw_writel(1, wdog_dd->base + WDT0_RST);
|
__raw_writel(1, wdog_dd->base + WDT0_RST);
|
||||||
wdog_dd->last_pet = sched_clock();
|
wdog_dd->last_pet = sched_clock();
|
||||||
printk(KERN_INFO "MSM Watchdog Initialized\n");
|
error = device_create_file(wdog_dd->dev, &dev_attr_disable);
|
||||||
|
if (error)
|
||||||
|
dev_err(wdog_dd->dev, "cannot create sysfs attribute\n");
|
||||||
|
if (wdog_dd->irq_ppi)
|
||||||
|
enable_percpu_irq(wdog_dd->bark_irq, 0);
|
||||||
|
dev_info(wdog_dd->dev, "MSM Watchdog Initialized\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue