blob: aabb644502a0b44ecdad503b000da1345762233b [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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154static int wdog_enable_set(const char *val, struct kernel_param *kp)
155{
156 int ret = 0;
157 int old_val = runtime_disable;
158
159 mutex_lock(&disable_lock);
160
161 if (!enable) {
162 printk(KERN_INFO "MSM Watchdog is not active.\n");
163 ret = -EINVAL;
164 goto done;
165 }
166
167 ret = param_set_int(val, kp);
168
169 if (ret)
170 goto done;
171
172 switch (runtime_disable) {
173
174 case 1:
175 if (!old_val) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700176 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700177 mb();
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700178 if (has_vic) {
179 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
180 } else {
181 disable_percpu_irq(WDT0_ACCSCSSNBARK_INT);
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800182 if (!appsbark_fiq) {
183 free_percpu_irq(WDT0_ACCSCSSNBARK_INT,
184 percpu_pdata);
185 free_percpu(percpu_pdata);
186 }
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700187 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188 enable = 0;
189 atomic_notifier_chain_unregister(&panic_notifier_list,
190 &panic_blk);
191 cancel_delayed_work(&dogwork_struct);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700192 /* may be suspended after the first write above */
193 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 printk(KERN_INFO "MSM Watchdog deactivated.\n");
195 }
196 break;
197
198 default:
199 runtime_disable = old_val;
200 ret = -EINVAL;
201 break;
202
203 }
204
205done:
206 mutex_unlock(&disable_lock);
207 return ret;
208}
209
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800210unsigned min_slack_ticks = UINT_MAX;
211unsigned long long min_slack_ns = ULLONG_MAX;
212
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213void pet_watchdog(void)
214{
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800215 int slack;
216 unsigned long long time_ns;
217 unsigned long long slack_ns;
218 unsigned long long bark_time_ns = bark_time * 1000000ULL;
219
220 slack = __raw_readl(msm_tmr0_base + WDT0_STS) >> 3;
221 slack = ((bark_time*WDT_HZ)/1000) - slack;
222 if (slack < min_slack_ticks)
223 min_slack_ticks = slack;
Rohit Vaswani085a9332011-09-28 18:57:24 -0700224 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800225 time_ns = sched_clock();
226 slack_ns = (last_pet + bark_time_ns) - time_ns;
227 if (slack_ns < min_slack_ns)
228 min_slack_ns = slack_ns;
229 last_pet = time_ns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230}
231
232static void pet_watchdog_work(struct work_struct *work)
233{
234 pet_watchdog();
235
236 if (enable)
237 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
238}
239
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700240static int msm_watchdog_remove(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700241{
242 if (enable) {
Rohit Vaswani085a9332011-09-28 18:57:24 -0700243 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700244 mb();
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700245 if (has_vic) {
246 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
247 } else {
248 disable_percpu_irq(WDT0_ACCSCSSNBARK_INT);
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800249 if (!appsbark_fiq) {
250 free_percpu_irq(WDT0_ACCSCSSNBARK_INT,
251 percpu_pdata);
252 free_percpu(percpu_pdata);
253 }
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700254 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255 enable = 0;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700256 /* In case we got suspended mid-exit */
257 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258 }
259 printk(KERN_INFO "MSM Watchdog Exit - Deactivated\n");
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700260 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261}
262
263static irqreturn_t wdog_bark_handler(int irq, void *dev_id)
264{
265 unsigned long nanosec_rem;
266 unsigned long long t = sched_clock();
267 struct task_struct *tsk;
268
269 nanosec_rem = do_div(t, 1000000000);
270 printk(KERN_INFO "Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t,
271 nanosec_rem / 1000);
272
273 nanosec_rem = do_div(last_pet, 1000000000);
274 printk(KERN_INFO "Watchdog last pet at %lu.%06lu\n", (unsigned long)
275 last_pet, nanosec_rem / 1000);
276
277 if (print_all_stacks) {
278
279 /* Suspend wdog until all stacks are printed */
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700280 msm_watchdog_suspend(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281
282 printk(KERN_INFO "Stack trace dump:\n");
283
284 for_each_process(tsk) {
285 printk(KERN_INFO "\nPID: %d, Name: %s\n",
286 tsk->pid, tsk->comm);
287 show_stack(tsk, NULL);
288 }
289
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700290 msm_watchdog_resume(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291 }
292
293 panic("Apps watchdog bark received!");
294 return IRQ_HANDLED;
295}
296
297#define SCM_SET_REGSAVE_CMD 0x2
298
Rohit Vaswani085a9332011-09-28 18:57:24 -0700299static void configure_bark_dump(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700300{
301 int ret;
302 struct {
303 unsigned addr;
304 int len;
305 } cmd_buf;
306
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700307 if (!appsbark) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308 scm_regsave = (void *)__get_free_page(GFP_KERNEL);
309
310 if (scm_regsave) {
311 cmd_buf.addr = __pa(scm_regsave);
312 cmd_buf.len = PAGE_SIZE;
313
314 ret = scm_call(SCM_SVC_UTIL, SCM_SET_REGSAVE_CMD,
315 &cmd_buf, sizeof(cmd_buf), NULL, 0);
316 if (ret)
317 pr_err("Setting register save address failed.\n"
318 "Registers won't be dumped on a dog "
319 "bite\n");
Rohit Vaswani085a9332011-09-28 18:57:24 -0700320 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321 pr_err("Allocating register save space failed\n"
322 "Registers won't be dumped on a dog bite\n");
323 /*
324 * No need to bail if allocation fails. Simply don't
325 * send the command, and the secure side will reset
326 * without saving registers.
327 */
Rohit Vaswani085a9332011-09-28 18:57:24 -0700328 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329 }
Rohit Vaswani085a9332011-09-28 18:57:24 -0700330}
331
332static void init_watchdog_work(struct work_struct *work)
333{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700334 u64 timeout = (bark_time * WDT_HZ)/1000;
Jeff Ohlstein5edb4ae2012-03-06 16:39:50 -0800335
336 configure_bark_dump();
337
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700338 __raw_writel(timeout, msm_tmr0_base + WDT0_BARK_TIME);
339 __raw_writel(timeout + 3*WDT_HZ, msm_tmr0_base + WDT0_BITE_TIME);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340
341 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
342
343 atomic_notifier_chain_register(&panic_notifier_list,
344 &panic_blk);
345
Rohit Vaswani085a9332011-09-28 18:57:24 -0700346 __raw_writel(1, msm_tmr0_base + WDT0_EN);
347 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348 last_pet = sched_clock();
349
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800350 if (!has_vic)
351 enable_percpu_irq(WDT0_ACCSCSSNBARK_INT, IRQ_TYPE_EDGE_RISING);
352
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700353 printk(KERN_INFO "MSM Watchdog Initialized\n");
354
355 return;
356}
357
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800358struct fiq_handler wdog_fh = {
359 .name = MODULE_NAME,
360};
361
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700362static int msm_watchdog_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700363{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700364 struct msm_watchdog_pdata *pdata = pdev->dev.platform_data;
365 int ret;
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800366 void *stack;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700367
368 if (!enable || !pdata || !pdata->pet_time || !pdata->bark_time) {
369 printk(KERN_INFO "MSM Watchdog Not Initialized\n");
370 return -ENODEV;
371 }
372
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700373 bark_time = pdata->bark_time;
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700374 has_vic = pdata->has_vic;
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800375 if (!pdata->has_secure) {
376 appsbark = 1;
377 appsbark_fiq = pdata->use_kernel_fiq;
378 }
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700379
380 msm_tmr0_base = msm_timer_get_timer0_base();
381
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700382 if (has_vic) {
383 ret = request_irq(WDT0_ACCSCSSNBARK_INT, wdog_bark_handler, 0,
384 "apps_wdog_bark", NULL);
385 if (ret)
386 return ret;
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800387 } else if (appsbark_fiq) {
388 claim_fiq(&wdog_fh);
389 set_fiq_handler(&msm_wdog_fiq_start, msm_wdog_fiq_length);
390 stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
391 if (!stack) {
392 pr_info("No free pages available - %s fails\n",
393 __func__);
394 return -ENOMEM;
395 }
396
397 msm_wdog_fiq_setup(stack);
398 gic_set_irq_secure(WDT0_ACCSCSSNBARK_INT);
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700399 } else {
400 percpu_pdata = alloc_percpu(struct msm_watchdog_pdata *);
401 if (!percpu_pdata) {
402 pr_err("%s: memory allocation failed for percpu data\n",
403 __func__);
404 return -ENOMEM;
405 }
Trilok Sonieecb28c2011-07-20 16:24:14 +0100406
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700407 *__this_cpu_ptr(percpu_pdata) = pdata;
408 /* Must request irq before sending scm command */
409 ret = request_percpu_irq(WDT0_ACCSCSSNBARK_INT,
410 wdog_bark_handler, "apps_wdog_bark", percpu_pdata);
411 if (ret) {
412 free_percpu(percpu_pdata);
413 return ret;
414 }
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700415 }
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700416
417 /*
418 * This is only temporary till SBLs turn on the XPUs
419 * This initialization will be done in SBLs on a later releases
420 */
421 if (cpu_is_msm9615())
422 __raw_writel(0xF, MSM_TCSR_BASE + TCSR_WDT_CFG);
423
Joel Kinge7ca6f72012-02-09 20:51:25 -0800424 if (pdata->needs_expired_enable)
425 __raw_writel(0x1, MSM_CLK_CTL_BASE + 0x3820);
426
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700427 delay_time = msecs_to_jiffies(pdata->pet_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700428 schedule_work_on(0, &init_dogwork_struct);
429 return 0;
430}
431
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700432static const struct dev_pm_ops msm_watchdog_dev_pm_ops = {
433 .suspend_noirq = msm_watchdog_suspend,
434 .resume_noirq = msm_watchdog_resume,
435};
436
437static struct platform_driver msm_watchdog_driver = {
438 .probe = msm_watchdog_probe,
439 .remove = msm_watchdog_remove,
440 .driver = {
441 .name = MODULE_NAME,
442 .owner = THIS_MODULE,
443 .pm = &msm_watchdog_dev_pm_ops,
444 },
445};
446
447static int init_watchdog(void)
448{
449 return platform_driver_register(&msm_watchdog_driver);
450}
451
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452late_initcall(init_watchdog);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453MODULE_DESCRIPTION("MSM Watchdog Driver");
454MODULE_VERSION("1.0");
455MODULE_LICENSE("GPL v2");