blob: 994dca6e06a30295b28deb4478bf80f721d6fbcd [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;
Rohit Vaswanic9fdd442012-03-19 14:18:32 -070050static bool has_vic;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051
52/*
53 * On the kernel command line specify
54 * msm_watchdog.enable=1 to enable the watchdog
55 * By default watchdog is turned on
56 */
57static int enable = 1;
58module_param(enable, int, 0);
59
60/*
61 * If the watchdog is enabled at bootup (enable=1),
62 * the runtime_disable sysfs node at
63 * /sys/module/msm_watchdog/runtime_disable
64 * can be used to deactivate the watchdog.
65 * This is a one-time setting. The watchdog
66 * cannot be re-enabled once it is disabled.
67 */
68static int runtime_disable;
69static DEFINE_MUTEX(disable_lock);
70static int wdog_enable_set(const char *val, struct kernel_param *kp);
71module_param_call(runtime_disable, wdog_enable_set, param_get_int,
72 &runtime_disable, 0644);
73
74/*
75 * On the kernel command line specify msm_watchdog.appsbark=1 to handle
76 * watchdog barks in Linux. By default barks are processed by the secure side.
77 */
78static int appsbark;
79module_param(appsbark, int, 0);
80
81/*
82 * Use /sys/module/msm_watchdog/parameters/print_all_stacks
83 * to control whether stacks of all running
84 * processes are printed when a wdog bark is received.
85 */
86static int print_all_stacks = 1;
87module_param(print_all_stacks, int, S_IRUGO | S_IWUSR);
88
89/* Area for context dump in secure mode */
90static void *scm_regsave;
91
Trilok Sonieecb28c2011-07-20 16:24:14 +010092static struct msm_watchdog_pdata __percpu **percpu_pdata;
93
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094static void pet_watchdog_work(struct work_struct *work);
95static void init_watchdog_work(struct work_struct *work);
96static DECLARE_DELAYED_WORK(dogwork_struct, pet_watchdog_work);
97static DECLARE_WORK(init_dogwork_struct, init_watchdog_work);
98
Jeff Ohlstein7e668552011-10-06 16:17:25 -070099static int msm_watchdog_suspend(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700101 if (!enable)
102 return 0;
103
Rohit Vaswani085a9332011-09-28 18:57:24 -0700104 __raw_writel(1, msm_tmr0_base + WDT0_RST);
105 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106 mb();
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700107 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700108}
109
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700110static int msm_watchdog_resume(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700112 if (!enable)
113 return 0;
114
115 __raw_writel(1, msm_tmr0_base + WDT0_EN);
116 __raw_writel(1, msm_tmr0_base + WDT0_RST);
117 mb();
118 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119}
120
121static int panic_wdog_handler(struct notifier_block *this,
122 unsigned long event, void *ptr)
123{
124 if (panic_timeout == 0) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700125 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 } else {
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700128 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700129 msm_tmr0_base + WDT0_BARK_TIME);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700130 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700131 msm_tmr0_base + WDT0_BITE_TIME);
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700132 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700133 }
134 return NOTIFY_DONE;
135}
136
137static struct notifier_block panic_blk = {
138 .notifier_call = panic_wdog_handler,
139};
140
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141static int wdog_enable_set(const char *val, struct kernel_param *kp)
142{
143 int ret = 0;
144 int old_val = runtime_disable;
145
146 mutex_lock(&disable_lock);
147
148 if (!enable) {
149 printk(KERN_INFO "MSM Watchdog is not active.\n");
150 ret = -EINVAL;
151 goto done;
152 }
153
154 ret = param_set_int(val, kp);
155
156 if (ret)
157 goto done;
158
159 switch (runtime_disable) {
160
161 case 1:
162 if (!old_val) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700163 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 mb();
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700165 if (has_vic) {
166 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
167 } else {
168 disable_percpu_irq(WDT0_ACCSCSSNBARK_INT);
169 free_percpu_irq(WDT0_ACCSCSSNBARK_INT,
170 percpu_pdata);
171 free_percpu(percpu_pdata);
172 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173 enable = 0;
174 atomic_notifier_chain_unregister(&panic_notifier_list,
175 &panic_blk);
176 cancel_delayed_work(&dogwork_struct);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700177 /* may be suspended after the first write above */
178 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700179 printk(KERN_INFO "MSM Watchdog deactivated.\n");
180 }
181 break;
182
183 default:
184 runtime_disable = old_val;
185 ret = -EINVAL;
186 break;
187
188 }
189
190done:
191 mutex_unlock(&disable_lock);
192 return ret;
193}
194
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800195unsigned min_slack_ticks = UINT_MAX;
196unsigned long long min_slack_ns = ULLONG_MAX;
197
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198void pet_watchdog(void)
199{
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800200 int slack;
201 unsigned long long time_ns;
202 unsigned long long slack_ns;
203 unsigned long long bark_time_ns = bark_time * 1000000ULL;
204
205 slack = __raw_readl(msm_tmr0_base + WDT0_STS) >> 3;
206 slack = ((bark_time*WDT_HZ)/1000) - slack;
207 if (slack < min_slack_ticks)
208 min_slack_ticks = slack;
Rohit Vaswani085a9332011-09-28 18:57:24 -0700209 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Jeff Ohlstein535280e2011-12-09 19:34:30 -0800210 time_ns = sched_clock();
211 slack_ns = (last_pet + bark_time_ns) - time_ns;
212 if (slack_ns < min_slack_ns)
213 min_slack_ns = slack_ns;
214 last_pet = time_ns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215}
216
217static void pet_watchdog_work(struct work_struct *work)
218{
219 pet_watchdog();
220
221 if (enable)
222 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
223}
224
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700225static int msm_watchdog_remove(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226{
227 if (enable) {
Rohit Vaswani085a9332011-09-28 18:57:24 -0700228 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229 mb();
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700230 if (has_vic) {
231 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
232 } else {
233 disable_percpu_irq(WDT0_ACCSCSSNBARK_INT);
234 free_percpu_irq(WDT0_ACCSCSSNBARK_INT, percpu_pdata);
235 free_percpu(percpu_pdata);
236 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700237 enable = 0;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700238 /* In case we got suspended mid-exit */
239 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700240 }
241 printk(KERN_INFO "MSM Watchdog Exit - Deactivated\n");
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700242 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243}
244
245static irqreturn_t wdog_bark_handler(int irq, void *dev_id)
246{
247 unsigned long nanosec_rem;
248 unsigned long long t = sched_clock();
249 struct task_struct *tsk;
250
251 nanosec_rem = do_div(t, 1000000000);
252 printk(KERN_INFO "Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t,
253 nanosec_rem / 1000);
254
255 nanosec_rem = do_div(last_pet, 1000000000);
256 printk(KERN_INFO "Watchdog last pet at %lu.%06lu\n", (unsigned long)
257 last_pet, nanosec_rem / 1000);
258
259 if (print_all_stacks) {
260
261 /* Suspend wdog until all stacks are printed */
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700262 msm_watchdog_suspend(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263
264 printk(KERN_INFO "Stack trace dump:\n");
265
266 for_each_process(tsk) {
267 printk(KERN_INFO "\nPID: %d, Name: %s\n",
268 tsk->pid, tsk->comm);
269 show_stack(tsk, NULL);
270 }
271
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700272 msm_watchdog_resume(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700273 }
274
275 panic("Apps watchdog bark received!");
276 return IRQ_HANDLED;
277}
278
279#define SCM_SET_REGSAVE_CMD 0x2
280
Rohit Vaswani085a9332011-09-28 18:57:24 -0700281static void configure_bark_dump(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282{
283 int ret;
284 struct {
285 unsigned addr;
286 int len;
287 } cmd_buf;
288
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700289 if (!appsbark) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700290 scm_regsave = (void *)__get_free_page(GFP_KERNEL);
291
292 if (scm_regsave) {
293 cmd_buf.addr = __pa(scm_regsave);
294 cmd_buf.len = PAGE_SIZE;
295
296 ret = scm_call(SCM_SVC_UTIL, SCM_SET_REGSAVE_CMD,
297 &cmd_buf, sizeof(cmd_buf), NULL, 0);
298 if (ret)
299 pr_err("Setting register save address failed.\n"
300 "Registers won't be dumped on a dog "
301 "bite\n");
Rohit Vaswani085a9332011-09-28 18:57:24 -0700302 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303 pr_err("Allocating register save space failed\n"
304 "Registers won't be dumped on a dog bite\n");
305 /*
306 * No need to bail if allocation fails. Simply don't
307 * send the command, and the secure side will reset
308 * without saving registers.
309 */
Rohit Vaswani085a9332011-09-28 18:57:24 -0700310 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311 }
Rohit Vaswani085a9332011-09-28 18:57:24 -0700312}
313
314static void init_watchdog_work(struct work_struct *work)
315{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700316 u64 timeout = (bark_time * WDT_HZ)/1000;
Jeff Ohlstein5edb4ae2012-03-06 16:39:50 -0800317
318 configure_bark_dump();
319
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700320 __raw_writel(timeout, msm_tmr0_base + WDT0_BARK_TIME);
321 __raw_writel(timeout + 3*WDT_HZ, msm_tmr0_base + WDT0_BITE_TIME);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700322
323 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
324
325 atomic_notifier_chain_register(&panic_notifier_list,
326 &panic_blk);
327
Rohit Vaswani085a9332011-09-28 18:57:24 -0700328 __raw_writel(1, msm_tmr0_base + WDT0_EN);
329 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700330 last_pet = sched_clock();
331
332 printk(KERN_INFO "MSM Watchdog Initialized\n");
333
334 return;
335}
336
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700337static int msm_watchdog_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700338{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700339 struct msm_watchdog_pdata *pdata = pdev->dev.platform_data;
340 int ret;
341
342 if (!enable || !pdata || !pdata->pet_time || !pdata->bark_time) {
343 printk(KERN_INFO "MSM Watchdog Not Initialized\n");
344 return -ENODEV;
345 }
346
347 if (!pdata->has_secure)
348 appsbark = 1;
349
350 bark_time = pdata->bark_time;
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700351 has_vic = pdata->has_vic;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700352
353 msm_tmr0_base = msm_timer_get_timer0_base();
354
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700355 if (has_vic) {
356 ret = request_irq(WDT0_ACCSCSSNBARK_INT, wdog_bark_handler, 0,
357 "apps_wdog_bark", NULL);
358 if (ret)
359 return ret;
360 } else {
361 percpu_pdata = alloc_percpu(struct msm_watchdog_pdata *);
362 if (!percpu_pdata) {
363 pr_err("%s: memory allocation failed for percpu data\n",
364 __func__);
365 return -ENOMEM;
366 }
Trilok Sonieecb28c2011-07-20 16:24:14 +0100367
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700368 *__this_cpu_ptr(percpu_pdata) = pdata;
369 /* Must request irq before sending scm command */
370 ret = request_percpu_irq(WDT0_ACCSCSSNBARK_INT,
371 wdog_bark_handler, "apps_wdog_bark", percpu_pdata);
372 if (ret) {
373 free_percpu(percpu_pdata);
374 return ret;
375 }
Trilok Sonieecb28c2011-07-20 16:24:14 +0100376
Rohit Vaswanic9fdd442012-03-19 14:18:32 -0700377 enable_percpu_irq(WDT0_ACCSCSSNBARK_INT, IRQ_TYPE_EDGE_RISING);
378 }
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700379
380 /*
381 * This is only temporary till SBLs turn on the XPUs
382 * This initialization will be done in SBLs on a later releases
383 */
384 if (cpu_is_msm9615())
385 __raw_writel(0xF, MSM_TCSR_BASE + TCSR_WDT_CFG);
386
Joel Kinge7ca6f72012-02-09 20:51:25 -0800387 if (pdata->needs_expired_enable)
388 __raw_writel(0x1, MSM_CLK_CTL_BASE + 0x3820);
389
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700390 delay_time = msecs_to_jiffies(pdata->pet_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391 schedule_work_on(0, &init_dogwork_struct);
392 return 0;
393}
394
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700395static const struct dev_pm_ops msm_watchdog_dev_pm_ops = {
396 .suspend_noirq = msm_watchdog_suspend,
397 .resume_noirq = msm_watchdog_resume,
398};
399
400static struct platform_driver msm_watchdog_driver = {
401 .probe = msm_watchdog_probe,
402 .remove = msm_watchdog_remove,
403 .driver = {
404 .name = MODULE_NAME,
405 .owner = THIS_MODULE,
406 .pm = &msm_watchdog_dev_pm_ops,
407 },
408};
409
410static int init_watchdog(void)
411{
412 return platform_driver_register(&msm_watchdog_driver);
413}
414
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415late_initcall(init_watchdog);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416MODULE_DESCRIPTION("MSM Watchdog Driver");
417MODULE_VERSION("1.0");
418MODULE_LICENSE("GPL v2");