blob: 2cff7f0b4615ea8d78368d2f54d0c15152fc17bb [file] [log] [blame]
Trilok Soni1e52e432012-01-13 18:06:14 +05301/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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>
Trilok Sonieecb28c2011-07-20 16:24:14 +010024#include <linux/percpu.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025#include <linux/interrupt.h>
Rohit Vaswaniead426f2012-01-05 20:24:52 -080026#include <asm/fiq.h>
27#include <asm/hardware/gic.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028#include <mach/msm_iomap.h>
29#include <asm/mach-types.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030#include <mach/scm.h>
Rohit Vaswani085a9332011-09-28 18:57:24 -070031#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032#include "msm_watchdog.h"
Rohit Vaswani085a9332011-09-28 18:57:24 -070033#include "timer.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034
Jeff Ohlstein7e668552011-10-06 16:17:25 -070035#define MODULE_NAME "msm_watchdog"
36
Rohit Vaswani085a9332011-09-28 18:57:24 -070037#define TCSR_WDT_CFG 0x30
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038
Rohit Vaswani085a9332011-09-28 18:57:24 -070039#define WDT0_RST 0x38
40#define WDT0_EN 0x40
Jeff Ohlstein535280e2011-12-09 19:34:30 -080041#define WDT0_STS 0x44
Rohit Vaswani085a9332011-09-28 18:57:24 -070042#define WDT0_BARK_TIME 0x4C
43#define WDT0_BITE_TIME 0x5C
44
Jeff Ohlstein7e668552011-10-06 16:17:25 -070045#define WDT_HZ 32768
46
Rohit Vaswaniead426f2012-01-05 20:24:52 -080047struct msm_watchdog_dump msm_dump_cpu_ctx;
48
Rohit Vaswani085a9332011-09-28 18:57:24 -070049static void __iomem *msm_tmr0_base;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051static unsigned long delay_time;
Jeff Ohlstein7e668552011-10-06 16:17:25 -070052static unsigned long bark_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053static unsigned long long last_pet;
Rohit Vaswanic9fdd442012-03-19 14:18:32 -070054static bool has_vic;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055
56/*
57 * On the kernel command line specify
58 * msm_watchdog.enable=1 to enable the watchdog
59 * By default watchdog is turned on
60 */
61static int enable = 1;
62module_param(enable, int, 0);
63
64/*
65 * If the watchdog is enabled at bootup (enable=1),
66 * the runtime_disable sysfs node at
67 * /sys/module/msm_watchdog/runtime_disable
68 * can be used to deactivate the watchdog.
69 * This is a one-time setting. The watchdog
70 * cannot be re-enabled once it is disabled.
71 */
72static int runtime_disable;
73static DEFINE_MUTEX(disable_lock);
74static int wdog_enable_set(const char *val, struct kernel_param *kp);
75module_param_call(runtime_disable, wdog_enable_set, param_get_int,
76 &runtime_disable, 0644);
77
78/*
79 * On the kernel command line specify msm_watchdog.appsbark=1 to handle
80 * watchdog barks in Linux. By default barks are processed by the secure side.
81 */
82static int appsbark;
83module_param(appsbark, int, 0);
84
Rohit Vaswaniead426f2012-01-05 20:24:52 -080085static int appsbark_fiq;
86
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087/*
88 * Use /sys/module/msm_watchdog/parameters/print_all_stacks
89 * to control whether stacks of all running
90 * processes are printed when a wdog bark is received.
91 */
92static int print_all_stacks = 1;
93module_param(print_all_stacks, int, S_IRUGO | S_IWUSR);
94
95/* Area for context dump in secure mode */
96static void *scm_regsave;
97
Trilok Sonieecb28c2011-07-20 16:24:14 +010098static struct msm_watchdog_pdata __percpu **percpu_pdata;
99
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100static void pet_watchdog_work(struct work_struct *work);
101static void init_watchdog_work(struct work_struct *work);
102static DECLARE_DELAYED_WORK(dogwork_struct, pet_watchdog_work);
103static DECLARE_WORK(init_dogwork_struct, init_watchdog_work);
104
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800105/* Called from the FIQ bark handler */
106void msm_wdog_bark_fin(void)
107{
108 pr_crit("\nApps Watchdog bark received - Calling Panic\n");
109 panic("Apps Watchdog Bark received\n");
110}
111
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700112static int msm_watchdog_suspend(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700114 if (!enable)
115 return 0;
116
Rohit Vaswani085a9332011-09-28 18:57:24 -0700117 __raw_writel(1, msm_tmr0_base + WDT0_RST);
118 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119 mb();
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700120 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121}
122
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700123static int msm_watchdog_resume(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700125 if (!enable)
126 return 0;
127
128 __raw_writel(1, msm_tmr0_base + WDT0_EN);
129 __raw_writel(1, msm_tmr0_base + WDT0_RST);
130 mb();
131 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132}
133
134static int panic_wdog_handler(struct notifier_block *this,
135 unsigned long event, void *ptr)
136{
137 if (panic_timeout == 0) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700138 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700139 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140 } else {
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700141 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700142 msm_tmr0_base + WDT0_BARK_TIME);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700143 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700144 msm_tmr0_base + WDT0_BITE_TIME);
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700145 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146 }
147 return NOTIFY_DONE;
148}
149
150static struct notifier_block panic_blk = {
151 .notifier_call = panic_wdog_handler,
152};
153
Jeff Ohlsteinb1e211e2012-05-01 18:55:05 -0700154struct wdog_disable_work_data {
155 struct work_struct work;
156 struct completion complete;
157};
158
159static void wdog_disable_work(struct work_struct *work)
160{
161 struct wdog_disable_work_data *work_data =
162 container_of(work, struct wdog_disable_work_data, work);
163 __raw_writel(0, msm_tmr0_base + WDT0_EN);
164 mb();
165 if (has_vic) {
166 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
167 } else {
168 disable_percpu_irq(WDT0_ACCSCSSNBARK_INT);
169 if (!appsbark_fiq) {
170 free_percpu_irq(WDT0_ACCSCSSNBARK_INT,
171 percpu_pdata);
172 free_percpu(percpu_pdata);
173 }
174 }
175 enable = 0;
176 atomic_notifier_chain_unregister(&panic_notifier_list, &panic_blk);
177 cancel_delayed_work(&dogwork_struct);
178 /* may be suspended after the first write above */
179 __raw_writel(0, msm_tmr0_base + WDT0_EN);
180 complete(&work_data->complete);
181 pr_info("MSM Watchdog deactivated.\n");
182}
183
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184static int wdog_enable_set(const char *val, struct kernel_param *kp)
185{
186 int ret = 0;
187 int old_val = runtime_disable;
Jeff Ohlsteinb1e211e2012-05-01 18:55:05 -0700188 struct wdog_disable_work_data work_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189
190 mutex_lock(&disable_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191 if (!enable) {
192 printk(KERN_INFO "MSM Watchdog is not active.\n");
193 ret = -EINVAL;
194 goto done;
195 }
196
197 ret = param_set_int(val, kp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 if (ret)
199 goto done;
200
Jeff Ohlsteinb1e211e2012-05-01 18:55:05 -0700201 if (runtime_disable == 1) {
202 if (old_val)
203 goto done;
204 init_completion(&work_data.complete);
205 INIT_WORK_ONSTACK(&work_data.work, wdog_disable_work);
206 schedule_work_on(0, &work_data.work);
207 wait_for_completion(&work_data.complete);
208 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209 runtime_disable = old_val;
210 ret = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212done:
213 mutex_unlock(&disable_lock);
214 return ret;
215}
216
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800217unsigned min_slack_ticks = UINT_MAX;
218unsigned long long min_slack_ns = ULLONG_MAX;
219
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220void pet_watchdog(void)
221{
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800222 int slack;
223 unsigned long long time_ns;
224 unsigned long long slack_ns;
225 unsigned long long bark_time_ns = bark_time * 1000000ULL;
226
Vikram Mulukutla76f08e02012-05-01 19:15:19 -0700227 if (!enable)
228 return;
229
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800230 slack = __raw_readl(msm_tmr0_base + WDT0_STS) >> 3;
231 slack = ((bark_time*WDT_HZ)/1000) - slack;
232 if (slack < min_slack_ticks)
233 min_slack_ticks = slack;
Rohit Vaswani085a9332011-09-28 18:57:24 -0700234 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800235 time_ns = sched_clock();
236 slack_ns = (last_pet + bark_time_ns) - time_ns;
237 if (slack_ns < min_slack_ns)
238 min_slack_ns = slack_ns;
239 last_pet = time_ns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700240}
241
242static void pet_watchdog_work(struct work_struct *work)
243{
244 pet_watchdog();
245
246 if (enable)
247 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
248}
249
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700250static int msm_watchdog_remove(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700251{
252 if (enable) {
Rohit Vaswani085a9332011-09-28 18:57:24 -0700253 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700254 mb();
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700255 if (has_vic) {
256 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
257 } else {
258 disable_percpu_irq(WDT0_ACCSCSSNBARK_INT);
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800259 if (!appsbark_fiq) {
260 free_percpu_irq(WDT0_ACCSCSSNBARK_INT,
261 percpu_pdata);
262 free_percpu(percpu_pdata);
263 }
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700264 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265 enable = 0;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700266 /* In case we got suspended mid-exit */
267 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268 }
269 printk(KERN_INFO "MSM Watchdog Exit - Deactivated\n");
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700270 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700271}
272
273static irqreturn_t wdog_bark_handler(int irq, void *dev_id)
274{
275 unsigned long nanosec_rem;
276 unsigned long long t = sched_clock();
277 struct task_struct *tsk;
278
279 nanosec_rem = do_div(t, 1000000000);
280 printk(KERN_INFO "Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t,
281 nanosec_rem / 1000);
282
283 nanosec_rem = do_div(last_pet, 1000000000);
284 printk(KERN_INFO "Watchdog last pet at %lu.%06lu\n", (unsigned long)
285 last_pet, nanosec_rem / 1000);
286
287 if (print_all_stacks) {
288
289 /* Suspend wdog until all stacks are printed */
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700290 msm_watchdog_suspend(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291
292 printk(KERN_INFO "Stack trace dump:\n");
293
294 for_each_process(tsk) {
295 printk(KERN_INFO "\nPID: %d, Name: %s\n",
296 tsk->pid, tsk->comm);
297 show_stack(tsk, NULL);
298 }
299
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700300 msm_watchdog_resume(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301 }
302
303 panic("Apps watchdog bark received!");
304 return IRQ_HANDLED;
305}
306
307#define SCM_SET_REGSAVE_CMD 0x2
308
Rohit Vaswani085a9332011-09-28 18:57:24 -0700309static void configure_bark_dump(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310{
311 int ret;
312 struct {
313 unsigned addr;
314 int len;
315 } cmd_buf;
316
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700317 if (!appsbark) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318 scm_regsave = (void *)__get_free_page(GFP_KERNEL);
319
320 if (scm_regsave) {
321 cmd_buf.addr = __pa(scm_regsave);
322 cmd_buf.len = PAGE_SIZE;
323
324 ret = scm_call(SCM_SVC_UTIL, SCM_SET_REGSAVE_CMD,
325 &cmd_buf, sizeof(cmd_buf), NULL, 0);
326 if (ret)
327 pr_err("Setting register save address failed.\n"
328 "Registers won't be dumped on a dog "
329 "bite\n");
Rohit Vaswani085a9332011-09-28 18:57:24 -0700330 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331 pr_err("Allocating register save space failed\n"
332 "Registers won't be dumped on a dog bite\n");
333 /*
334 * No need to bail if allocation fails. Simply don't
335 * send the command, and the secure side will reset
336 * without saving registers.
337 */
Rohit Vaswani085a9332011-09-28 18:57:24 -0700338 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700339 }
Rohit Vaswani085a9332011-09-28 18:57:24 -0700340}
341
Jeff Ohlsteinb1e211e2012-05-01 18:55:05 -0700342struct fiq_handler wdog_fh = {
343 .name = MODULE_NAME,
344};
345
Rohit Vaswani085a9332011-09-28 18:57:24 -0700346static void init_watchdog_work(struct work_struct *work)
347{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700348 u64 timeout = (bark_time * WDT_HZ)/1000;
Jeff Ohlsteinb1e211e2012-05-01 18:55:05 -0700349 void *stack;
350 int ret;
351
352 if (has_vic) {
353 ret = request_irq(WDT0_ACCSCSSNBARK_INT, wdog_bark_handler, 0,
354 "apps_wdog_bark", NULL);
355 if (ret)
356 return;
357 } else if (appsbark_fiq) {
358 claim_fiq(&wdog_fh);
359 set_fiq_handler(&msm_wdog_fiq_start, msm_wdog_fiq_length);
360 stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
361 if (!stack) {
362 pr_info("No free pages available - %s fails\n",
363 __func__);
364 return;
365 }
366
367 msm_wdog_fiq_setup(stack);
368 gic_set_irq_secure(WDT0_ACCSCSSNBARK_INT);
369 } else {
370 percpu_pdata = alloc_percpu(struct msm_watchdog_pdata *);
371 if (!percpu_pdata) {
372 pr_err("%s: memory allocation failed for percpu data\n",
373 __func__);
374 return;
375 }
376
377 /* Must request irq before sending scm command */
378 ret = request_percpu_irq(WDT0_ACCSCSSNBARK_INT,
379 wdog_bark_handler, "apps_wdog_bark", percpu_pdata);
380 if (ret) {
381 free_percpu(percpu_pdata);
382 return;
383 }
384 }
Jeff Ohlstein5edb4ae2012-03-06 16:39:50 -0800385
386 configure_bark_dump();
387
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700388 __raw_writel(timeout, msm_tmr0_base + WDT0_BARK_TIME);
389 __raw_writel(timeout + 3*WDT_HZ, msm_tmr0_base + WDT0_BITE_TIME);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390
391 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
392
393 atomic_notifier_chain_register(&panic_notifier_list,
394 &panic_blk);
395
Rohit Vaswani085a9332011-09-28 18:57:24 -0700396 __raw_writel(1, msm_tmr0_base + WDT0_EN);
397 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398 last_pet = sched_clock();
399
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800400 if (!has_vic)
401 enable_percpu_irq(WDT0_ACCSCSSNBARK_INT, IRQ_TYPE_EDGE_RISING);
402
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700403 printk(KERN_INFO "MSM Watchdog Initialized\n");
404
405 return;
406}
407
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700408static int msm_watchdog_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700410 struct msm_watchdog_pdata *pdata = pdev->dev.platform_data;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700411
412 if (!enable || !pdata || !pdata->pet_time || !pdata->bark_time) {
413 printk(KERN_INFO "MSM Watchdog Not Initialized\n");
414 return -ENODEV;
415 }
416
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700417 bark_time = pdata->bark_time;
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700418 has_vic = pdata->has_vic;
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800419 if (!pdata->has_secure) {
420 appsbark = 1;
421 appsbark_fiq = pdata->use_kernel_fiq;
422 }
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700423
424 msm_tmr0_base = msm_timer_get_timer0_base();
425
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700426 /*
427 * This is only temporary till SBLs turn on the XPUs
428 * This initialization will be done in SBLs on a later releases
429 */
430 if (cpu_is_msm9615())
431 __raw_writel(0xF, MSM_TCSR_BASE + TCSR_WDT_CFG);
432
Joel Kinge7ca6f72012-02-09 20:51:25 -0800433 if (pdata->needs_expired_enable)
434 __raw_writel(0x1, MSM_CLK_CTL_BASE + 0x3820);
435
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700436 delay_time = msecs_to_jiffies(pdata->pet_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437 schedule_work_on(0, &init_dogwork_struct);
438 return 0;
439}
440
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700441static const struct dev_pm_ops msm_watchdog_dev_pm_ops = {
442 .suspend_noirq = msm_watchdog_suspend,
443 .resume_noirq = msm_watchdog_resume,
444};
445
446static struct platform_driver msm_watchdog_driver = {
447 .probe = msm_watchdog_probe,
448 .remove = msm_watchdog_remove,
449 .driver = {
450 .name = MODULE_NAME,
451 .owner = THIS_MODULE,
452 .pm = &msm_watchdog_dev_pm_ops,
453 },
454};
455
456static int init_watchdog(void)
457{
458 return platform_driver_register(&msm_watchdog_driver);
459}
460
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700461late_initcall(init_watchdog);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700462MODULE_DESCRIPTION("MSM Watchdog Driver");
463MODULE_VERSION("1.0");
464MODULE_LICENSE("GPL v2");