blob: f04148a64baf01ca56a976f49fcf083835075a03 [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
Jeff Ohlstein7e668552011-10-06 16:17:25 -070032#define MODULE_NAME "msm_watchdog"
33
Rohit Vaswani085a9332011-09-28 18:57:24 -070034#define TCSR_WDT_CFG 0x30
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035
Rohit Vaswani085a9332011-09-28 18:57:24 -070036#define WDT0_RST 0x38
37#define WDT0_EN 0x40
Jeff Ohlstein535280e2011-12-09 19:34:30 -080038#define WDT0_STS 0x44
Rohit Vaswani085a9332011-09-28 18:57:24 -070039#define WDT0_BARK_TIME 0x4C
40#define WDT0_BITE_TIME 0x5C
41
Jeff Ohlstein7e668552011-10-06 16:17:25 -070042#define WDT_HZ 32768
43
Rohit Vaswani085a9332011-09-28 18:57:24 -070044static void __iomem *msm_tmr0_base;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046static unsigned long delay_time;
Jeff Ohlstein7e668552011-10-06 16:17:25 -070047static unsigned long bark_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048static unsigned long long last_pet;
49
50/*
51 * On the kernel command line specify
52 * msm_watchdog.enable=1 to enable the watchdog
53 * By default watchdog is turned on
54 */
55static int enable = 1;
56module_param(enable, int, 0);
57
58/*
59 * If the watchdog is enabled at bootup (enable=1),
60 * the runtime_disable sysfs node at
61 * /sys/module/msm_watchdog/runtime_disable
62 * can be used to deactivate the watchdog.
63 * This is a one-time setting. The watchdog
64 * cannot be re-enabled once it is disabled.
65 */
66static int runtime_disable;
67static DEFINE_MUTEX(disable_lock);
68static int wdog_enable_set(const char *val, struct kernel_param *kp);
69module_param_call(runtime_disable, wdog_enable_set, param_get_int,
70 &runtime_disable, 0644);
71
72/*
73 * On the kernel command line specify msm_watchdog.appsbark=1 to handle
74 * watchdog barks in Linux. By default barks are processed by the secure side.
75 */
76static int appsbark;
77module_param(appsbark, int, 0);
78
79/*
80 * Use /sys/module/msm_watchdog/parameters/print_all_stacks
81 * to control whether stacks of all running
82 * processes are printed when a wdog bark is received.
83 */
84static int print_all_stacks = 1;
85module_param(print_all_stacks, int, S_IRUGO | S_IWUSR);
86
87/* Area for context dump in secure mode */
88static void *scm_regsave;
89
90static void pet_watchdog_work(struct work_struct *work);
91static void init_watchdog_work(struct work_struct *work);
92static DECLARE_DELAYED_WORK(dogwork_struct, pet_watchdog_work);
93static DECLARE_WORK(init_dogwork_struct, init_watchdog_work);
94
Jeff Ohlstein7e668552011-10-06 16:17:25 -070095static int msm_watchdog_suspend(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096{
Jeff Ohlstein7e668552011-10-06 16:17:25 -070097 if (!enable)
98 return 0;
99
Rohit Vaswani085a9332011-09-28 18:57:24 -0700100 __raw_writel(1, msm_tmr0_base + WDT0_RST);
101 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102 mb();
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700103 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104}
105
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700106static int msm_watchdog_resume(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700108 if (!enable)
109 return 0;
110
111 __raw_writel(1, msm_tmr0_base + WDT0_EN);
112 __raw_writel(1, msm_tmr0_base + WDT0_RST);
113 mb();
114 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115}
116
117static int panic_wdog_handler(struct notifier_block *this,
118 unsigned long event, void *ptr)
119{
120 if (panic_timeout == 0) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700121 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123 } else {
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700124 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700125 msm_tmr0_base + WDT0_BARK_TIME);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700126 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700127 msm_tmr0_base + WDT0_BITE_TIME);
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700128 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129 }
130 return NOTIFY_DONE;
131}
132
133static struct notifier_block panic_blk = {
134 .notifier_call = panic_wdog_handler,
135};
136
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137static int wdog_enable_set(const char *val, struct kernel_param *kp)
138{
139 int ret = 0;
140 int old_val = runtime_disable;
141
142 mutex_lock(&disable_lock);
143
144 if (!enable) {
145 printk(KERN_INFO "MSM Watchdog is not active.\n");
146 ret = -EINVAL;
147 goto done;
148 }
149
150 ret = param_set_int(val, kp);
151
152 if (ret)
153 goto done;
154
155 switch (runtime_disable) {
156
157 case 1:
158 if (!old_val) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700159 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
162 enable = 0;
163 atomic_notifier_chain_unregister(&panic_notifier_list,
164 &panic_blk);
165 cancel_delayed_work(&dogwork_struct);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700166 /* may be suspended after the first write above */
167 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 printk(KERN_INFO "MSM Watchdog deactivated.\n");
169 }
170 break;
171
172 default:
173 runtime_disable = old_val;
174 ret = -EINVAL;
175 break;
176
177 }
178
179done:
180 mutex_unlock(&disable_lock);
181 return ret;
182}
183
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800184unsigned min_slack_ticks = UINT_MAX;
185unsigned long long min_slack_ns = ULLONG_MAX;
186
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187void pet_watchdog(void)
188{
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800189 int slack;
190 unsigned long long time_ns;
191 unsigned long long slack_ns;
192 unsigned long long bark_time_ns = bark_time * 1000000ULL;
193
194 slack = __raw_readl(msm_tmr0_base + WDT0_STS) >> 3;
195 slack = ((bark_time*WDT_HZ)/1000) - slack;
196 if (slack < min_slack_ticks)
197 min_slack_ticks = slack;
Rohit Vaswani085a9332011-09-28 18:57:24 -0700198 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800199 time_ns = sched_clock();
200 slack_ns = (last_pet + bark_time_ns) - time_ns;
201 if (slack_ns < min_slack_ns)
202 min_slack_ns = slack_ns;
203 last_pet = time_ns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204}
205
206static void pet_watchdog_work(struct work_struct *work)
207{
208 pet_watchdog();
209
210 if (enable)
211 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
212}
213
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700214static int msm_watchdog_remove(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215{
216 if (enable) {
Rohit Vaswani085a9332011-09-28 18:57:24 -0700217 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
220 enable = 0;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700221 /* In case we got suspended mid-exit */
222 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223 }
224 printk(KERN_INFO "MSM Watchdog Exit - Deactivated\n");
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700225 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226}
227
228static irqreturn_t wdog_bark_handler(int irq, void *dev_id)
229{
230 unsigned long nanosec_rem;
231 unsigned long long t = sched_clock();
232 struct task_struct *tsk;
233
234 nanosec_rem = do_div(t, 1000000000);
235 printk(KERN_INFO "Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t,
236 nanosec_rem / 1000);
237
238 nanosec_rem = do_div(last_pet, 1000000000);
239 printk(KERN_INFO "Watchdog last pet at %lu.%06lu\n", (unsigned long)
240 last_pet, nanosec_rem / 1000);
241
242 if (print_all_stacks) {
243
244 /* Suspend wdog until all stacks are printed */
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700245 msm_watchdog_suspend(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700246
247 printk(KERN_INFO "Stack trace dump:\n");
248
249 for_each_process(tsk) {
250 printk(KERN_INFO "\nPID: %d, Name: %s\n",
251 tsk->pid, tsk->comm);
252 show_stack(tsk, NULL);
253 }
254
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700255 msm_watchdog_resume(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256 }
257
258 panic("Apps watchdog bark received!");
259 return IRQ_HANDLED;
260}
261
262#define SCM_SET_REGSAVE_CMD 0x2
263
Rohit Vaswani085a9332011-09-28 18:57:24 -0700264static void configure_bark_dump(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265{
266 int ret;
267 struct {
268 unsigned addr;
269 int len;
270 } cmd_buf;
271
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700272 if (!appsbark) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700273 scm_regsave = (void *)__get_free_page(GFP_KERNEL);
274
275 if (scm_regsave) {
276 cmd_buf.addr = __pa(scm_regsave);
277 cmd_buf.len = PAGE_SIZE;
278
279 ret = scm_call(SCM_SVC_UTIL, SCM_SET_REGSAVE_CMD,
280 &cmd_buf, sizeof(cmd_buf), NULL, 0);
281 if (ret)
282 pr_err("Setting register save address failed.\n"
283 "Registers won't be dumped on a dog "
284 "bite\n");
Rohit Vaswani085a9332011-09-28 18:57:24 -0700285 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700286 pr_err("Allocating register save space failed\n"
287 "Registers won't be dumped on a dog bite\n");
288 /*
289 * No need to bail if allocation fails. Simply don't
290 * send the command, and the secure side will reset
291 * without saving registers.
292 */
Rohit Vaswani085a9332011-09-28 18:57:24 -0700293 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 }
Rohit Vaswani085a9332011-09-28 18:57:24 -0700295}
296
297static void init_watchdog_work(struct work_struct *work)
298{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700299 u64 timeout = (bark_time * WDT_HZ)/1000;
300 __raw_writel(timeout, msm_tmr0_base + WDT0_BARK_TIME);
301 __raw_writel(timeout + 3*WDT_HZ, msm_tmr0_base + WDT0_BITE_TIME);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700302
303 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
304
305 atomic_notifier_chain_register(&panic_notifier_list,
306 &panic_blk);
307
Rohit Vaswani085a9332011-09-28 18:57:24 -0700308 __raw_writel(1, msm_tmr0_base + WDT0_EN);
309 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310 last_pet = sched_clock();
311
312 printk(KERN_INFO "MSM Watchdog Initialized\n");
313
314 return;
315}
316
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700317static int msm_watchdog_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700319 struct msm_watchdog_pdata *pdata = pdev->dev.platform_data;
320 int ret;
321
322 if (!enable || !pdata || !pdata->pet_time || !pdata->bark_time) {
323 printk(KERN_INFO "MSM Watchdog Not Initialized\n");
324 return -ENODEV;
325 }
326
327 if (!pdata->has_secure)
328 appsbark = 1;
329
330 bark_time = pdata->bark_time;
331
332 msm_tmr0_base = msm_timer_get_timer0_base();
333
334 /* Must request irq before sending scm command */
335 ret = request_irq(WDT0_ACCSCSSNBARK_INT, wdog_bark_handler, 0,
336 "apps_wdog_bark", NULL);
337 if (ret)
338 return -EINVAL;
339
340 /*
341 * This is only temporary till SBLs turn on the XPUs
342 * This initialization will be done in SBLs on a later releases
343 */
344 if (cpu_is_msm9615())
345 __raw_writel(0xF, MSM_TCSR_BASE + TCSR_WDT_CFG);
346
347 configure_bark_dump();
348
349 delay_time = msecs_to_jiffies(pdata->pet_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700350 schedule_work_on(0, &init_dogwork_struct);
351 return 0;
352}
353
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700354static const struct dev_pm_ops msm_watchdog_dev_pm_ops = {
355 .suspend_noirq = msm_watchdog_suspend,
356 .resume_noirq = msm_watchdog_resume,
357};
358
359static struct platform_driver msm_watchdog_driver = {
360 .probe = msm_watchdog_probe,
361 .remove = msm_watchdog_remove,
362 .driver = {
363 .name = MODULE_NAME,
364 .owner = THIS_MODULE,
365 .pm = &msm_watchdog_dev_pm_ops,
366 },
367};
368
369static int init_watchdog(void)
370{
371 return platform_driver_register(&msm_watchdog_driver);
372}
373
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700374late_initcall(init_watchdog);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700375MODULE_DESCRIPTION("MSM Watchdog Driver");
376MODULE_VERSION("1.0");
377MODULE_LICENSE("GPL v2");