blob: b47142641a07b97c8bf547bb45aba3d7c1b29a7a [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>
Rohit Vaswani9cea7ca2012-07-06 19:54:24 -070030#include <asm/cacheflush.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031#include <mach/scm.h>
Rohit Vaswani085a9332011-09-28 18:57:24 -070032#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033#include "msm_watchdog.h"
Rohit Vaswani085a9332011-09-28 18:57:24 -070034#include "timer.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035
Jeff Ohlstein7e668552011-10-06 16:17:25 -070036#define MODULE_NAME "msm_watchdog"
37
Rohit Vaswani085a9332011-09-28 18:57:24 -070038#define TCSR_WDT_CFG 0x30
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039
Rohit Vaswani085a9332011-09-28 18:57:24 -070040#define WDT0_RST 0x38
41#define WDT0_EN 0x40
Jeff Ohlstein535280e2011-12-09 19:34:30 -080042#define WDT0_STS 0x44
Rohit Vaswani085a9332011-09-28 18:57:24 -070043#define WDT0_BARK_TIME 0x4C
44#define WDT0_BITE_TIME 0x5C
45
Jeff Ohlstein7e668552011-10-06 16:17:25 -070046#define WDT_HZ 32768
47
Rohit Vaswaniead426f2012-01-05 20:24:52 -080048struct msm_watchdog_dump msm_dump_cpu_ctx;
49
Rohit Vaswani085a9332011-09-28 18:57:24 -070050static void __iomem *msm_tmr0_base;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052static unsigned long delay_time;
Jeff Ohlstein7e668552011-10-06 16:17:25 -070053static unsigned long bark_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054static unsigned long long last_pet;
Rohit Vaswanic9fdd442012-03-19 14:18:32 -070055static bool has_vic;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056
57/*
58 * On the kernel command line specify
59 * msm_watchdog.enable=1 to enable the watchdog
60 * By default watchdog is turned on
61 */
62static int enable = 1;
63module_param(enable, int, 0);
64
65/*
66 * If the watchdog is enabled at bootup (enable=1),
67 * the runtime_disable sysfs node at
68 * /sys/module/msm_watchdog/runtime_disable
69 * can be used to deactivate the watchdog.
70 * This is a one-time setting. The watchdog
71 * cannot be re-enabled once it is disabled.
72 */
73static int runtime_disable;
74static DEFINE_MUTEX(disable_lock);
75static int wdog_enable_set(const char *val, struct kernel_param *kp);
76module_param_call(runtime_disable, wdog_enable_set, param_get_int,
77 &runtime_disable, 0644);
78
79/*
80 * On the kernel command line specify msm_watchdog.appsbark=1 to handle
81 * watchdog barks in Linux. By default barks are processed by the secure side.
82 */
83static int appsbark;
84module_param(appsbark, int, 0);
85
Rohit Vaswaniead426f2012-01-05 20:24:52 -080086static int appsbark_fiq;
87
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070088/*
89 * Use /sys/module/msm_watchdog/parameters/print_all_stacks
90 * to control whether stacks of all running
91 * processes are printed when a wdog bark is received.
92 */
93static int print_all_stacks = 1;
94module_param(print_all_stacks, int, S_IRUGO | S_IWUSR);
95
96/* Area for context dump in secure mode */
97static void *scm_regsave;
98
Trilok Sonieecb28c2011-07-20 16:24:14 +010099static struct msm_watchdog_pdata __percpu **percpu_pdata;
100
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101static void pet_watchdog_work(struct work_struct *work);
102static void init_watchdog_work(struct work_struct *work);
103static DECLARE_DELAYED_WORK(dogwork_struct, pet_watchdog_work);
104static DECLARE_WORK(init_dogwork_struct, init_watchdog_work);
105
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800106/* Called from the FIQ bark handler */
107void msm_wdog_bark_fin(void)
108{
Rohit Vaswani9cea7ca2012-07-06 19:54:24 -0700109 flush_cache_all();
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800110 pr_crit("\nApps Watchdog bark received - Calling Panic\n");
111 panic("Apps Watchdog Bark received\n");
112}
113
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700114static int msm_watchdog_suspend(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700116 if (!enable)
117 return 0;
118
Rohit Vaswani085a9332011-09-28 18:57:24 -0700119 __raw_writel(1, msm_tmr0_base + WDT0_RST);
120 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121 mb();
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700122 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123}
124
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700125static int msm_watchdog_resume(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700127 if (!enable)
128 return 0;
129
130 __raw_writel(1, msm_tmr0_base + WDT0_EN);
131 __raw_writel(1, msm_tmr0_base + WDT0_RST);
132 mb();
133 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134}
135
136static int panic_wdog_handler(struct notifier_block *this,
137 unsigned long event, void *ptr)
138{
139 if (panic_timeout == 0) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700140 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142 } else {
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_BARK_TIME);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700145 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700146 msm_tmr0_base + WDT0_BITE_TIME);
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700147 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148 }
149 return NOTIFY_DONE;
150}
151
152static struct notifier_block panic_blk = {
153 .notifier_call = panic_wdog_handler,
154};
155
Jeff Ohlsteinb1e211e2012-05-01 18:55:05 -0700156struct wdog_disable_work_data {
157 struct work_struct work;
158 struct completion complete;
159};
160
161static void wdog_disable_work(struct work_struct *work)
162{
163 struct wdog_disable_work_data *work_data =
164 container_of(work, struct wdog_disable_work_data, work);
165 __raw_writel(0, msm_tmr0_base + WDT0_EN);
166 mb();
167 if (has_vic) {
168 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
169 } else {
170 disable_percpu_irq(WDT0_ACCSCSSNBARK_INT);
171 if (!appsbark_fiq) {
172 free_percpu_irq(WDT0_ACCSCSSNBARK_INT,
173 percpu_pdata);
174 free_percpu(percpu_pdata);
175 }
176 }
177 enable = 0;
178 atomic_notifier_chain_unregister(&panic_notifier_list, &panic_blk);
179 cancel_delayed_work(&dogwork_struct);
180 /* may be suspended after the first write above */
181 __raw_writel(0, msm_tmr0_base + WDT0_EN);
182 complete(&work_data->complete);
183 pr_info("MSM Watchdog deactivated.\n");
184}
185
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186static int wdog_enable_set(const char *val, struct kernel_param *kp)
187{
188 int ret = 0;
189 int old_val = runtime_disable;
Jeff Ohlsteinb1e211e2012-05-01 18:55:05 -0700190 struct wdog_disable_work_data work_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191
192 mutex_lock(&disable_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193 if (!enable) {
194 printk(KERN_INFO "MSM Watchdog is not active.\n");
195 ret = -EINVAL;
196 goto done;
197 }
198
199 ret = param_set_int(val, kp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 if (ret)
201 goto done;
202
Jeff Ohlsteinb1e211e2012-05-01 18:55:05 -0700203 if (runtime_disable == 1) {
204 if (old_val)
205 goto done;
206 init_completion(&work_data.complete);
207 INIT_WORK_ONSTACK(&work_data.work, wdog_disable_work);
208 schedule_work_on(0, &work_data.work);
209 wait_for_completion(&work_data.complete);
210 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 runtime_disable = old_val;
212 ret = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214done:
215 mutex_unlock(&disable_lock);
216 return ret;
217}
218
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800219unsigned min_slack_ticks = UINT_MAX;
220unsigned long long min_slack_ns = ULLONG_MAX;
221
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222void pet_watchdog(void)
223{
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800224 int slack;
225 unsigned long long time_ns;
226 unsigned long long slack_ns;
227 unsigned long long bark_time_ns = bark_time * 1000000ULL;
228
Vikram Mulukutla76f08e02012-05-01 19:15:19 -0700229 if (!enable)
230 return;
231
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800232 slack = __raw_readl(msm_tmr0_base + WDT0_STS) >> 3;
233 slack = ((bark_time*WDT_HZ)/1000) - slack;
234 if (slack < min_slack_ticks)
235 min_slack_ticks = slack;
Rohit Vaswani085a9332011-09-28 18:57:24 -0700236 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800237 time_ns = sched_clock();
238 slack_ns = (last_pet + bark_time_ns) - time_ns;
239 if (slack_ns < min_slack_ns)
240 min_slack_ns = slack_ns;
241 last_pet = time_ns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242}
243
244static void pet_watchdog_work(struct work_struct *work)
245{
246 pet_watchdog();
247
248 if (enable)
249 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
250}
251
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252static irqreturn_t wdog_bark_handler(int irq, void *dev_id)
253{
254 unsigned long nanosec_rem;
255 unsigned long long t = sched_clock();
256 struct task_struct *tsk;
257
258 nanosec_rem = do_div(t, 1000000000);
259 printk(KERN_INFO "Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t,
260 nanosec_rem / 1000);
261
262 nanosec_rem = do_div(last_pet, 1000000000);
263 printk(KERN_INFO "Watchdog last pet at %lu.%06lu\n", (unsigned long)
264 last_pet, nanosec_rem / 1000);
265
266 if (print_all_stacks) {
267
268 /* Suspend wdog until all stacks are printed */
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700269 msm_watchdog_suspend(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700270
271 printk(KERN_INFO "Stack trace dump:\n");
272
273 for_each_process(tsk) {
274 printk(KERN_INFO "\nPID: %d, Name: %s\n",
275 tsk->pid, tsk->comm);
276 show_stack(tsk, NULL);
277 }
278
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700279 msm_watchdog_resume(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280 }
281
282 panic("Apps watchdog bark received!");
283 return IRQ_HANDLED;
284}
285
286#define SCM_SET_REGSAVE_CMD 0x2
287
Rohit Vaswani085a9332011-09-28 18:57:24 -0700288static void configure_bark_dump(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700289{
290 int ret;
291 struct {
292 unsigned addr;
293 int len;
294 } cmd_buf;
295
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700296 if (!appsbark) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297 scm_regsave = (void *)__get_free_page(GFP_KERNEL);
298
299 if (scm_regsave) {
300 cmd_buf.addr = __pa(scm_regsave);
301 cmd_buf.len = PAGE_SIZE;
302
303 ret = scm_call(SCM_SVC_UTIL, SCM_SET_REGSAVE_CMD,
304 &cmd_buf, sizeof(cmd_buf), NULL, 0);
305 if (ret)
306 pr_err("Setting register save address failed.\n"
307 "Registers won't be dumped on a dog "
308 "bite\n");
Rohit Vaswani085a9332011-09-28 18:57:24 -0700309 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310 pr_err("Allocating register save space failed\n"
311 "Registers won't be dumped on a dog bite\n");
312 /*
313 * No need to bail if allocation fails. Simply don't
314 * send the command, and the secure side will reset
315 * without saving registers.
316 */
Rohit Vaswani085a9332011-09-28 18:57:24 -0700317 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318 }
Rohit Vaswani085a9332011-09-28 18:57:24 -0700319}
320
Jeff Ohlsteinb1e211e2012-05-01 18:55:05 -0700321struct fiq_handler wdog_fh = {
322 .name = MODULE_NAME,
323};
324
Rohit Vaswani085a9332011-09-28 18:57:24 -0700325static void init_watchdog_work(struct work_struct *work)
326{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700327 u64 timeout = (bark_time * WDT_HZ)/1000;
Jeff Ohlsteinb1e211e2012-05-01 18:55:05 -0700328 void *stack;
329 int ret;
330
331 if (has_vic) {
332 ret = request_irq(WDT0_ACCSCSSNBARK_INT, wdog_bark_handler, 0,
333 "apps_wdog_bark", NULL);
334 if (ret)
335 return;
336 } else if (appsbark_fiq) {
337 claim_fiq(&wdog_fh);
338 set_fiq_handler(&msm_wdog_fiq_start, msm_wdog_fiq_length);
339 stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
340 if (!stack) {
341 pr_info("No free pages available - %s fails\n",
342 __func__);
343 return;
344 }
345
346 msm_wdog_fiq_setup(stack);
347 gic_set_irq_secure(WDT0_ACCSCSSNBARK_INT);
348 } else {
349 percpu_pdata = alloc_percpu(struct msm_watchdog_pdata *);
350 if (!percpu_pdata) {
351 pr_err("%s: memory allocation failed for percpu data\n",
352 __func__);
353 return;
354 }
355
356 /* Must request irq before sending scm command */
357 ret = request_percpu_irq(WDT0_ACCSCSSNBARK_INT,
358 wdog_bark_handler, "apps_wdog_bark", percpu_pdata);
359 if (ret) {
360 free_percpu(percpu_pdata);
361 return;
362 }
363 }
Jeff Ohlstein5edb4ae2012-03-06 16:39:50 -0800364
365 configure_bark_dump();
366
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700367 __raw_writel(timeout, msm_tmr0_base + WDT0_BARK_TIME);
368 __raw_writel(timeout + 3*WDT_HZ, msm_tmr0_base + WDT0_BITE_TIME);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369
370 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
371
372 atomic_notifier_chain_register(&panic_notifier_list,
373 &panic_blk);
374
Rohit Vaswani085a9332011-09-28 18:57:24 -0700375 __raw_writel(1, msm_tmr0_base + WDT0_EN);
376 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377 last_pet = sched_clock();
378
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800379 if (!has_vic)
380 enable_percpu_irq(WDT0_ACCSCSSNBARK_INT, IRQ_TYPE_EDGE_RISING);
381
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382 printk(KERN_INFO "MSM Watchdog Initialized\n");
383
384 return;
385}
386
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700387static int msm_watchdog_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700389 struct msm_watchdog_pdata *pdata = pdev->dev.platform_data;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700390
391 if (!enable || !pdata || !pdata->pet_time || !pdata->bark_time) {
392 printk(KERN_INFO "MSM Watchdog Not Initialized\n");
393 return -ENODEV;
394 }
395
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700396 bark_time = pdata->bark_time;
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700397 has_vic = pdata->has_vic;
Rohit Vaswaniead426f2012-01-05 20:24:52 -0800398 if (!pdata->has_secure) {
399 appsbark = 1;
400 appsbark_fiq = pdata->use_kernel_fiq;
401 }
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700402
403 msm_tmr0_base = msm_timer_get_timer0_base();
404
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700405 /*
406 * This is only temporary till SBLs turn on the XPUs
407 * This initialization will be done in SBLs on a later releases
408 */
409 if (cpu_is_msm9615())
410 __raw_writel(0xF, MSM_TCSR_BASE + TCSR_WDT_CFG);
411
Joel Kinge7ca6f72012-02-09 20:51:25 -0800412 if (pdata->needs_expired_enable)
413 __raw_writel(0x1, MSM_CLK_CTL_BASE + 0x3820);
414
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700415 delay_time = msecs_to_jiffies(pdata->pet_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416 schedule_work_on(0, &init_dogwork_struct);
417 return 0;
418}
419
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700420static const struct dev_pm_ops msm_watchdog_dev_pm_ops = {
421 .suspend_noirq = msm_watchdog_suspend,
422 .resume_noirq = msm_watchdog_resume,
423};
424
425static struct platform_driver msm_watchdog_driver = {
426 .probe = msm_watchdog_probe,
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700427 .driver = {
428 .name = MODULE_NAME,
429 .owner = THIS_MODULE,
430 .pm = &msm_watchdog_dev_pm_ops,
431 },
432};
433
434static int init_watchdog(void)
435{
436 return platform_driver_register(&msm_watchdog_driver);
437}
438
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700439late_initcall(init_watchdog);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440MODULE_DESCRIPTION("MSM Watchdog Driver");
441MODULE_VERSION("1.0");
442MODULE_LICENSE("GPL v2");