blob: c517178707f9ec419ebecfd60ef9a5f45fd96806 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/io.h>
18#include <linux/delay.h>
19#include <linux/workqueue.h>
20#include <linux/pm.h>
21#include <linux/mfd/pmic8058.h>
22#include <linux/jiffies.h>
23#include <linux/suspend.h>
24#include <linux/interrupt.h>
25#include <mach/msm_iomap.h>
26#include <asm/mach-types.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027#include <mach/scm.h>
Rohit Vaswani085a9332011-09-28 18:57:24 -070028#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029#include "msm_watchdog.h"
Rohit Vaswani085a9332011-09-28 18:57:24 -070030#include "timer.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
Rohit Vaswani085a9332011-09-28 18:57:24 -070032#define TCSR_WDT_CFG 0x30
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033
Rohit Vaswani085a9332011-09-28 18:57:24 -070034#define WDT0_RST 0x38
35#define WDT0_EN 0x40
36#define WDT0_BARK_TIME 0x4C
37#define WDT0_BITE_TIME 0x5C
38
39static void __iomem *msm_tmr0_base;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040
41/* Watchdog pet interval in ms */
42#define PET_DELAY 3000
43static unsigned long delay_time;
44static unsigned long long last_pet;
45
46/*
47 * On the kernel command line specify
48 * msm_watchdog.enable=1 to enable the watchdog
49 * By default watchdog is turned on
50 */
51static int enable = 1;
52module_param(enable, int, 0);
53
54/*
55 * If the watchdog is enabled at bootup (enable=1),
56 * the runtime_disable sysfs node at
57 * /sys/module/msm_watchdog/runtime_disable
58 * can be used to deactivate the watchdog.
59 * This is a one-time setting. The watchdog
60 * cannot be re-enabled once it is disabled.
61 */
62static int runtime_disable;
63static DEFINE_MUTEX(disable_lock);
64static int wdog_enable_set(const char *val, struct kernel_param *kp);
65module_param_call(runtime_disable, wdog_enable_set, param_get_int,
66 &runtime_disable, 0644);
67
68/*
69 * On the kernel command line specify msm_watchdog.appsbark=1 to handle
70 * watchdog barks in Linux. By default barks are processed by the secure side.
71 */
72static int appsbark;
73module_param(appsbark, int, 0);
74
75/*
76 * Use /sys/module/msm_watchdog/parameters/print_all_stacks
77 * to control whether stacks of all running
78 * processes are printed when a wdog bark is received.
79 */
80static int print_all_stacks = 1;
81module_param(print_all_stacks, int, S_IRUGO | S_IWUSR);
82
83/* Area for context dump in secure mode */
84static void *scm_regsave;
85
86static void pet_watchdog_work(struct work_struct *work);
87static void init_watchdog_work(struct work_struct *work);
88static DECLARE_DELAYED_WORK(dogwork_struct, pet_watchdog_work);
89static DECLARE_WORK(init_dogwork_struct, init_watchdog_work);
90
91static int msm_watchdog_suspend(void)
92{
Rohit Vaswani085a9332011-09-28 18:57:24 -070093 __raw_writel(1, msm_tmr0_base + WDT0_RST);
94 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095 mb();
96 return NOTIFY_DONE;
97}
98static int msm_watchdog_resume(void)
99{
Rohit Vaswani085a9332011-09-28 18:57:24 -0700100 __raw_writel(1, msm_tmr0_base + WDT0_EN);
101 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102 return NOTIFY_DONE;
103}
104
105static int msm_watchdog_power_event(struct notifier_block *this,
106 unsigned long event, void *ptr)
107{
108 switch (event) {
109 case PM_POST_HIBERNATION:
110 case PM_POST_SUSPEND:
111 return msm_watchdog_resume();
112 case PM_HIBERNATION_PREPARE:
113 case PM_SUSPEND_PREPARE:
114 return msm_watchdog_suspend();
115 default:
116 return NOTIFY_DONE;
117 }
118}
119
120static int panic_wdog_handler(struct notifier_block *this,
121 unsigned long event, void *ptr)
122{
123 if (panic_timeout == 0) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700124 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126 } else {
Rohit Vaswani085a9332011-09-28 18:57:24 -0700127 __raw_writel(32768 * (panic_timeout + 4),
128 msm_tmr0_base + WDT0_BARK_TIME);
129 __raw_writel(32768 * (panic_timeout + 4),
130 msm_tmr0_base + WDT0_BITE_TIME);
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700131 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132 }
133 return NOTIFY_DONE;
134}
135
136static struct notifier_block panic_blk = {
137 .notifier_call = panic_wdog_handler,
138};
139
140static struct notifier_block msm_watchdog_power_notifier = {
141 .notifier_call = msm_watchdog_power_event,
142};
143
144static int wdog_enable_set(const char *val, struct kernel_param *kp)
145{
146 int ret = 0;
147 int old_val = runtime_disable;
148
149 mutex_lock(&disable_lock);
150
151 if (!enable) {
152 printk(KERN_INFO "MSM Watchdog is not active.\n");
153 ret = -EINVAL;
154 goto done;
155 }
156
157 ret = param_set_int(val, kp);
158
159 if (ret)
160 goto done;
161
162 switch (runtime_disable) {
163
164 case 1:
165 if (!old_val) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700166 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167 unregister_pm_notifier(&msm_watchdog_power_notifier);
168
169 /* may be suspended after the first write above */
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700170 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
173 enable = 0;
174 atomic_notifier_chain_unregister(&panic_notifier_list,
175 &panic_blk);
176 cancel_delayed_work(&dogwork_struct);
177 printk(KERN_INFO "MSM Watchdog deactivated.\n");
178 }
179 break;
180
181 default:
182 runtime_disable = old_val;
183 ret = -EINVAL;
184 break;
185
186 }
187
188done:
189 mutex_unlock(&disable_lock);
190 return ret;
191}
192
193void pet_watchdog(void)
194{
Rohit Vaswani085a9332011-09-28 18:57:24 -0700195 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 last_pet = sched_clock();
197}
198
199static void pet_watchdog_work(struct work_struct *work)
200{
201 pet_watchdog();
202
203 if (enable)
204 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
205}
206
207static void __exit exit_watchdog(void)
208{
209 if (enable) {
Rohit Vaswani085a9332011-09-28 18:57:24 -0700210 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 unregister_pm_notifier(&msm_watchdog_power_notifier);
Rohit Vaswani085a9332011-09-28 18:57:24 -0700212 /* In case we got suspended mid-exit */
213 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
216 enable = 0;
217 }
218 printk(KERN_INFO "MSM Watchdog Exit - Deactivated\n");
219}
220
221static irqreturn_t wdog_bark_handler(int irq, void *dev_id)
222{
223 unsigned long nanosec_rem;
224 unsigned long long t = sched_clock();
225 struct task_struct *tsk;
226
227 nanosec_rem = do_div(t, 1000000000);
228 printk(KERN_INFO "Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t,
229 nanosec_rem / 1000);
230
231 nanosec_rem = do_div(last_pet, 1000000000);
232 printk(KERN_INFO "Watchdog last pet at %lu.%06lu\n", (unsigned long)
233 last_pet, nanosec_rem / 1000);
234
235 if (print_all_stacks) {
236
237 /* Suspend wdog until all stacks are printed */
238 msm_watchdog_suspend();
239
240 printk(KERN_INFO "Stack trace dump:\n");
241
242 for_each_process(tsk) {
243 printk(KERN_INFO "\nPID: %d, Name: %s\n",
244 tsk->pid, tsk->comm);
245 show_stack(tsk, NULL);
246 }
247
248 msm_watchdog_resume();
249 }
250
251 panic("Apps watchdog bark received!");
252 return IRQ_HANDLED;
253}
254
255#define SCM_SET_REGSAVE_CMD 0x2
256
Rohit Vaswani085a9332011-09-28 18:57:24 -0700257static void configure_bark_dump(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258{
259 int ret;
260 struct {
261 unsigned addr;
262 int len;
263 } cmd_buf;
264
Rohit Vaswani085a9332011-09-28 18:57:24 -0700265 if (!appsbark && !cpu_is_msm9615()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700266 scm_regsave = (void *)__get_free_page(GFP_KERNEL);
267
268 if (scm_regsave) {
269 cmd_buf.addr = __pa(scm_regsave);
270 cmd_buf.len = PAGE_SIZE;
271
272 ret = scm_call(SCM_SVC_UTIL, SCM_SET_REGSAVE_CMD,
273 &cmd_buf, sizeof(cmd_buf), NULL, 0);
274 if (ret)
275 pr_err("Setting register save address failed.\n"
276 "Registers won't be dumped on a dog "
277 "bite\n");
Rohit Vaswani085a9332011-09-28 18:57:24 -0700278 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279 pr_err("Allocating register save space failed\n"
280 "Registers won't be dumped on a dog bite\n");
281 /*
282 * No need to bail if allocation fails. Simply don't
283 * send the command, and the secure side will reset
284 * without saving registers.
285 */
Rohit Vaswani085a9332011-09-28 18:57:24 -0700286 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287 }
Rohit Vaswani085a9332011-09-28 18:57:24 -0700288}
289
290static void init_watchdog_work(struct work_struct *work)
291{
292 int ret;
293
294 if (!enable) {
295 printk(KERN_INFO "MSM Watchdog Not Initialized\n");
296 return;
297 }
298
299 msm_tmr0_base = msm_timer_get_timer0_base();
300
301 /* Must request irq before sending scm command */
302 ret = request_irq(WDT0_ACCSCSSNBARK_INT, wdog_bark_handler, 0,
303 "apps_wdog_bark", NULL);
304 if (ret)
305 return;
306
307 /*
308 * This is only temporary till SBLs turn on the XPUs
309 * This initialization will be done in SBLs on a later releases
310 */
311 if (cpu_is_msm9615())
312 __raw_writel(0xF, MSM_TCSR_BASE + TCSR_WDT_CFG);
313
314 configure_bark_dump();
315
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316 delay_time = msecs_to_jiffies(PET_DELAY);
317
318 /* 32768 ticks = 1 second */
319 if (machine_is_msm8960_sim()) {
Rohit Vaswani085a9332011-09-28 18:57:24 -0700320 __raw_writel(32768*8, msm_tmr0_base + WDT0_BARK_TIME);
321 __raw_writel(32768*10, msm_tmr0_base + WDT0_BITE_TIME);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700322 } else {
Rohit Vaswani085a9332011-09-28 18:57:24 -0700323 __raw_writel(32768*4, msm_tmr0_base + WDT0_BARK_TIME);
324 __raw_writel(32768*5, msm_tmr0_base + WDT0_BITE_TIME);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 }
326
327 ret = register_pm_notifier(&msm_watchdog_power_notifier);
328 if (ret) {
329 free_irq(WDT0_ACCSCSSNBARK_INT, NULL);
330 return;
331 }
332
333 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
334
335 atomic_notifier_chain_register(&panic_notifier_list,
336 &panic_blk);
337
Rohit Vaswani085a9332011-09-28 18:57:24 -0700338 __raw_writel(1, msm_tmr0_base + WDT0_EN);
339 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340 last_pet = sched_clock();
341
342 printk(KERN_INFO "MSM Watchdog Initialized\n");
343
344 return;
345}
346
347static int __init init_watchdog(void)
348{
349 schedule_work_on(0, &init_dogwork_struct);
350 return 0;
351}
352
353late_initcall(init_watchdog);
354module_exit(exit_watchdog);
355MODULE_DESCRIPTION("MSM Watchdog Driver");
356MODULE_VERSION("1.0");
357MODULE_LICENSE("GPL v2");