blob: 4303f83c8f2ba8ac0348c06417a3db14173fb7fa [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;
307 __raw_writel(timeout, msm_tmr0_base + WDT0_BARK_TIME);
308 __raw_writel(timeout + 3*WDT_HZ, msm_tmr0_base + WDT0_BITE_TIME);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700309
310 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
311
312 atomic_notifier_chain_register(&panic_notifier_list,
313 &panic_blk);
314
Rohit Vaswani085a9332011-09-28 18:57:24 -0700315 __raw_writel(1, msm_tmr0_base + WDT0_EN);
316 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700317 last_pet = sched_clock();
318
319 printk(KERN_INFO "MSM Watchdog Initialized\n");
320
321 return;
322}
323
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700324static int msm_watchdog_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700326 struct msm_watchdog_pdata *pdata = pdev->dev.platform_data;
327 int ret;
328
329 if (!enable || !pdata || !pdata->pet_time || !pdata->bark_time) {
330 printk(KERN_INFO "MSM Watchdog Not Initialized\n");
331 return -ENODEV;
332 }
333
334 if (!pdata->has_secure)
335 appsbark = 1;
336
337 bark_time = pdata->bark_time;
338
339 msm_tmr0_base = msm_timer_get_timer0_base();
340
Trilok Sonieecb28c2011-07-20 16:24:14 +0100341 percpu_pdata = alloc_percpu(struct msm_watchdog_pdata *);
342 if (!percpu_pdata) {
343 pr_err("%s: memory allocation failed for percpu data\n",
344 __func__);
345 return -ENOMEM;
346 }
347
348 *__this_cpu_ptr(percpu_pdata) = pdata;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700349 /* Must request irq before sending scm command */
Trilok Sonieecb28c2011-07-20 16:24:14 +0100350 ret = request_percpu_irq(WDT0_ACCSCSSNBARK_INT, wdog_bark_handler,
351 "apps_wdog_bark", percpu_pdata);
352 if (ret) {
353 free_percpu(percpu_pdata);
354 return ret;
355 }
356
Trilok Soni1e52e432012-01-13 18:06:14 +0530357 enable_percpu_irq(WDT0_ACCSCSSNBARK_INT, IRQ_TYPE_EDGE_RISING);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700358
359 /*
360 * This is only temporary till SBLs turn on the XPUs
361 * This initialization will be done in SBLs on a later releases
362 */
363 if (cpu_is_msm9615())
364 __raw_writel(0xF, MSM_TCSR_BASE + TCSR_WDT_CFG);
365
366 configure_bark_dump();
367
368 delay_time = msecs_to_jiffies(pdata->pet_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369 schedule_work_on(0, &init_dogwork_struct);
370 return 0;
371}
372
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700373static const struct dev_pm_ops msm_watchdog_dev_pm_ops = {
374 .suspend_noirq = msm_watchdog_suspend,
375 .resume_noirq = msm_watchdog_resume,
376};
377
378static struct platform_driver msm_watchdog_driver = {
379 .probe = msm_watchdog_probe,
380 .remove = msm_watchdog_remove,
381 .driver = {
382 .name = MODULE_NAME,
383 .owner = THIS_MODULE,
384 .pm = &msm_watchdog_dev_pm_ops,
385 },
386};
387
388static int init_watchdog(void)
389{
390 return platform_driver_register(&msm_watchdog_driver);
391}
392
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700393late_initcall(init_watchdog);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394MODULE_DESCRIPTION("MSM Watchdog Driver");
395MODULE_VERSION("1.0");
396MODULE_LICENSE("GPL v2");