blob: 28b5995a357be4ef9313fe7098565f63790081c1 [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>
26#include <mach/msm_iomap.h>
27#include <asm/mach-types.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028#include <mach/scm.h>
Rohit Vaswani085a9332011-09-28 18:57:24 -070029#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030#include "msm_watchdog.h"
Rohit Vaswani085a9332011-09-28 18:57:24 -070031#include "timer.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032
Jeff Ohlstein7e668552011-10-06 16:17:25 -070033#define MODULE_NAME "msm_watchdog"
34
Rohit Vaswani085a9332011-09-28 18:57:24 -070035#define TCSR_WDT_CFG 0x30
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036
Rohit Vaswani085a9332011-09-28 18:57:24 -070037#define WDT0_RST 0x38
38#define WDT0_EN 0x40
Jeff Ohlstein535280e2011-12-09 19:34:30 -080039#define WDT0_STS 0x44
Rohit Vaswani085a9332011-09-28 18:57:24 -070040#define WDT0_BARK_TIME 0x4C
41#define WDT0_BITE_TIME 0x5C
42
Jeff Ohlstein7e668552011-10-06 16:17:25 -070043#define WDT_HZ 32768
44
Rohit Vaswani085a9332011-09-28 18:57:24 -070045static void __iomem *msm_tmr0_base;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047static unsigned long delay_time;
Jeff Ohlstein7e668552011-10-06 16:17:25 -070048static unsigned long bark_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049static unsigned long long last_pet;
50
51/*
52 * On the kernel command line specify
53 * msm_watchdog.enable=1 to enable the watchdog
54 * By default watchdog is turned on
55 */
56static int enable = 1;
57module_param(enable, int, 0);
58
59/*
60 * If the watchdog is enabled at bootup (enable=1),
61 * the runtime_disable sysfs node at
62 * /sys/module/msm_watchdog/runtime_disable
63 * can be used to deactivate the watchdog.
64 * This is a one-time setting. The watchdog
65 * cannot be re-enabled once it is disabled.
66 */
67static int runtime_disable;
68static DEFINE_MUTEX(disable_lock);
69static int wdog_enable_set(const char *val, struct kernel_param *kp);
70module_param_call(runtime_disable, wdog_enable_set, param_get_int,
71 &runtime_disable, 0644);
72
73/*
74 * On the kernel command line specify msm_watchdog.appsbark=1 to handle
75 * watchdog barks in Linux. By default barks are processed by the secure side.
76 */
77static int appsbark;
78module_param(appsbark, int, 0);
79
80/*
81 * Use /sys/module/msm_watchdog/parameters/print_all_stacks
82 * to control whether stacks of all running
83 * processes are printed when a wdog bark is received.
84 */
85static int print_all_stacks = 1;
86module_param(print_all_stacks, int, S_IRUGO | S_IWUSR);
87
88/* Area for context dump in secure mode */
89static void *scm_regsave;
90
Trilok Sonieecb28c2011-07-20 16:24:14 +010091static struct msm_watchdog_pdata __percpu **percpu_pdata;
92
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070093static void pet_watchdog_work(struct work_struct *work);
94static void init_watchdog_work(struct work_struct *work);
95static DECLARE_DELAYED_WORK(dogwork_struct, pet_watchdog_work);
96static DECLARE_WORK(init_dogwork_struct, init_watchdog_work);
97
Jeff Ohlstein7e668552011-10-06 16:17:25 -070098static int msm_watchdog_suspend(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700100 if (!enable)
101 return 0;
102
Rohit Vaswani085a9332011-09-28 18:57:24 -0700103 __raw_writel(1, msm_tmr0_base + WDT0_RST);
104 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105 mb();
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700106 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107}
108
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700109static int msm_watchdog_resume(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700111 if (!enable)
112 return 0;
113
114 __raw_writel(1, msm_tmr0_base + WDT0_EN);
115 __raw_writel(1, msm_tmr0_base + WDT0_RST);
116 mb();
117 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118}
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 {
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700127 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700128 msm_tmr0_base + WDT0_BARK_TIME);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700129 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700130 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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140static int wdog_enable_set(const char *val, struct kernel_param *kp)
141{
142 int ret = 0;
143 int old_val = runtime_disable;
144
145 mutex_lock(&disable_lock);
146
147 if (!enable) {
148 printk(KERN_INFO "MSM Watchdog is not active.\n");
149 ret = -EINVAL;
150 goto done;
151 }
152
153 ret = param_set_int(val, kp);
154
155 if (ret)
156 goto done;
157
158 switch (runtime_disable) {
159
160 case 1:
161 if (!old_val) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700162 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700163 mb();
Trilok Sonieecb28c2011-07-20 16:24:14 +0100164 disable_percpu_irq(WDT0_ACCSCSSNBARK_INT);
165 free_percpu_irq(WDT0_ACCSCSSNBARK_INT, percpu_pdata);
166 free_percpu(percpu_pdata);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167 enable = 0;
168 atomic_notifier_chain_unregister(&panic_notifier_list,
169 &panic_blk);
170 cancel_delayed_work(&dogwork_struct);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700171 /* may be suspended after the first write above */
172 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173 printk(KERN_INFO "MSM Watchdog deactivated.\n");
174 }
175 break;
176
177 default:
178 runtime_disable = old_val;
179 ret = -EINVAL;
180 break;
181
182 }
183
184done:
185 mutex_unlock(&disable_lock);
186 return ret;
187}
188
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800189unsigned min_slack_ticks = UINT_MAX;
190unsigned long long min_slack_ns = ULLONG_MAX;
191
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192void pet_watchdog(void)
193{
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800194 int slack;
195 unsigned long long time_ns;
196 unsigned long long slack_ns;
197 unsigned long long bark_time_ns = bark_time * 1000000ULL;
198
199 slack = __raw_readl(msm_tmr0_base + WDT0_STS) >> 3;
200 slack = ((bark_time*WDT_HZ)/1000) - slack;
201 if (slack < min_slack_ticks)
202 min_slack_ticks = slack;
Rohit Vaswani085a9332011-09-28 18:57:24 -0700203 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800204 time_ns = sched_clock();
205 slack_ns = (last_pet + bark_time_ns) - time_ns;
206 if (slack_ns < min_slack_ns)
207 min_slack_ns = slack_ns;
208 last_pet = time_ns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209}
210
211static void pet_watchdog_work(struct work_struct *work)
212{
213 pet_watchdog();
214
215 if (enable)
216 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
217}
218
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700219static int msm_watchdog_remove(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220{
221 if (enable) {
Rohit Vaswani085a9332011-09-28 18:57:24 -0700222 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223 mb();
Trilok Sonieecb28c2011-07-20 16:24:14 +0100224 disable_percpu_irq(WDT0_ACCSCSSNBARK_INT);
225 free_percpu_irq(WDT0_ACCSCSSNBARK_INT, percpu_pdata);
226 free_percpu(percpu_pdata);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 enable = 0;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700228 /* In case we got suspended mid-exit */
229 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230 }
231 printk(KERN_INFO "MSM Watchdog Exit - Deactivated\n");
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700232 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700233}
234
235static irqreturn_t wdog_bark_handler(int irq, void *dev_id)
236{
237 unsigned long nanosec_rem;
238 unsigned long long t = sched_clock();
239 struct task_struct *tsk;
240
241 nanosec_rem = do_div(t, 1000000000);
242 printk(KERN_INFO "Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t,
243 nanosec_rem / 1000);
244
245 nanosec_rem = do_div(last_pet, 1000000000);
246 printk(KERN_INFO "Watchdog last pet at %lu.%06lu\n", (unsigned long)
247 last_pet, nanosec_rem / 1000);
248
249 if (print_all_stacks) {
250
251 /* Suspend wdog until all stacks are printed */
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700252 msm_watchdog_suspend(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253
254 printk(KERN_INFO "Stack trace dump:\n");
255
256 for_each_process(tsk) {
257 printk(KERN_INFO "\nPID: %d, Name: %s\n",
258 tsk->pid, tsk->comm);
259 show_stack(tsk, NULL);
260 }
261
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700262 msm_watchdog_resume(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263 }
264
265 panic("Apps watchdog bark received!");
266 return IRQ_HANDLED;
267}
268
269#define SCM_SET_REGSAVE_CMD 0x2
270
Rohit Vaswani085a9332011-09-28 18:57:24 -0700271static void configure_bark_dump(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272{
273 int ret;
274 struct {
275 unsigned addr;
276 int len;
277 } cmd_buf;
278
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700279 if (!appsbark) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280 scm_regsave = (void *)__get_free_page(GFP_KERNEL);
281
282 if (scm_regsave) {
283 cmd_buf.addr = __pa(scm_regsave);
284 cmd_buf.len = PAGE_SIZE;
285
286 ret = scm_call(SCM_SVC_UTIL, SCM_SET_REGSAVE_CMD,
287 &cmd_buf, sizeof(cmd_buf), NULL, 0);
288 if (ret)
289 pr_err("Setting register save address failed.\n"
290 "Registers won't be dumped on a dog "
291 "bite\n");
Rohit Vaswani085a9332011-09-28 18:57:24 -0700292 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 pr_err("Allocating register save space failed\n"
294 "Registers won't be dumped on a dog bite\n");
295 /*
296 * No need to bail if allocation fails. Simply don't
297 * send the command, and the secure side will reset
298 * without saving registers.
299 */
Rohit Vaswani085a9332011-09-28 18:57:24 -0700300 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301 }
Rohit Vaswani085a9332011-09-28 18:57:24 -0700302}
303
304static void init_watchdog_work(struct work_struct *work)
305{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700306 u64 timeout = (bark_time * WDT_HZ)/1000;
Jeff Ohlstein5edb4ae2012-03-06 16:39:50 -0800307
308 configure_bark_dump();
309
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700310 __raw_writel(timeout, msm_tmr0_base + WDT0_BARK_TIME);
311 __raw_writel(timeout + 3*WDT_HZ, msm_tmr0_base + WDT0_BITE_TIME);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700312
313 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
314
315 atomic_notifier_chain_register(&panic_notifier_list,
316 &panic_blk);
317
Rohit Vaswani085a9332011-09-28 18:57:24 -0700318 __raw_writel(1, msm_tmr0_base + WDT0_EN);
319 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320 last_pet = sched_clock();
321
322 printk(KERN_INFO "MSM Watchdog Initialized\n");
323
324 return;
325}
326
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700327static int msm_watchdog_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700328{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700329 struct msm_watchdog_pdata *pdata = pdev->dev.platform_data;
330 int ret;
331
332 if (!enable || !pdata || !pdata->pet_time || !pdata->bark_time) {
333 printk(KERN_INFO "MSM Watchdog Not Initialized\n");
334 return -ENODEV;
335 }
336
337 if (!pdata->has_secure)
338 appsbark = 1;
339
340 bark_time = pdata->bark_time;
341
342 msm_tmr0_base = msm_timer_get_timer0_base();
343
Trilok Sonieecb28c2011-07-20 16:24:14 +0100344 percpu_pdata = alloc_percpu(struct msm_watchdog_pdata *);
345 if (!percpu_pdata) {
346 pr_err("%s: memory allocation failed for percpu data\n",
347 __func__);
348 return -ENOMEM;
349 }
350
351 *__this_cpu_ptr(percpu_pdata) = pdata;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700352 /* Must request irq before sending scm command */
Trilok Sonieecb28c2011-07-20 16:24:14 +0100353 ret = request_percpu_irq(WDT0_ACCSCSSNBARK_INT, wdog_bark_handler,
354 "apps_wdog_bark", percpu_pdata);
355 if (ret) {
356 free_percpu(percpu_pdata);
357 return ret;
358 }
359
Trilok Soni1e52e432012-01-13 18:06:14 +0530360 enable_percpu_irq(WDT0_ACCSCSSNBARK_INT, IRQ_TYPE_EDGE_RISING);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700361
362 /*
363 * This is only temporary till SBLs turn on the XPUs
364 * This initialization will be done in SBLs on a later releases
365 */
366 if (cpu_is_msm9615())
367 __raw_writel(0xF, MSM_TCSR_BASE + TCSR_WDT_CFG);
368
Joel Kinge7ca6f72012-02-09 20:51:25 -0800369 if (pdata->needs_expired_enable)
370 __raw_writel(0x1, MSM_CLK_CTL_BASE + 0x3820);
371
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700372 delay_time = msecs_to_jiffies(pdata->pet_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373 schedule_work_on(0, &init_dogwork_struct);
374 return 0;
375}
376
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700377static const struct dev_pm_ops msm_watchdog_dev_pm_ops = {
378 .suspend_noirq = msm_watchdog_suspend,
379 .resume_noirq = msm_watchdog_resume,
380};
381
382static struct platform_driver msm_watchdog_driver = {
383 .probe = msm_watchdog_probe,
384 .remove = msm_watchdog_remove,
385 .driver = {
386 .name = MODULE_NAME,
387 .owner = THIS_MODULE,
388 .pm = &msm_watchdog_dev_pm_ops,
389 },
390};
391
392static int init_watchdog(void)
393{
394 return platform_driver_register(&msm_watchdog_driver);
395}
396
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700397late_initcall(init_watchdog);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398MODULE_DESCRIPTION("MSM Watchdog Driver");
399MODULE_VERSION("1.0");
400MODULE_LICENSE("GPL v2");