blob: a1316b77f1832c42bfd1b6ef51e5366975a52c9d [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
38#define WDT0_BARK_TIME 0x4C
39#define WDT0_BITE_TIME 0x5C
40
Jeff Ohlstein7e668552011-10-06 16:17:25 -070041#define WDT_HZ 32768
42
Rohit Vaswani085a9332011-09-28 18:57:24 -070043static void __iomem *msm_tmr0_base;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045static unsigned long delay_time;
Jeff Ohlstein7e668552011-10-06 16:17:25 -070046static unsigned long bark_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047static unsigned long long last_pet;
48
49/*
50 * On the kernel command line specify
51 * msm_watchdog.enable=1 to enable the watchdog
52 * By default watchdog is turned on
53 */
54static int enable = 1;
55module_param(enable, int, 0);
56
57/*
58 * If the watchdog is enabled at bootup (enable=1),
59 * the runtime_disable sysfs node at
60 * /sys/module/msm_watchdog/runtime_disable
61 * can be used to deactivate the watchdog.
62 * This is a one-time setting. The watchdog
63 * cannot be re-enabled once it is disabled.
64 */
65static int runtime_disable;
66static DEFINE_MUTEX(disable_lock);
67static int wdog_enable_set(const char *val, struct kernel_param *kp);
68module_param_call(runtime_disable, wdog_enable_set, param_get_int,
69 &runtime_disable, 0644);
70
71/*
72 * On the kernel command line specify msm_watchdog.appsbark=1 to handle
73 * watchdog barks in Linux. By default barks are processed by the secure side.
74 */
75static int appsbark;
76module_param(appsbark, int, 0);
77
78/*
79 * Use /sys/module/msm_watchdog/parameters/print_all_stacks
80 * to control whether stacks of all running
81 * processes are printed when a wdog bark is received.
82 */
83static int print_all_stacks = 1;
84module_param(print_all_stacks, int, S_IRUGO | S_IWUSR);
85
86/* Area for context dump in secure mode */
87static void *scm_regsave;
88
89static void pet_watchdog_work(struct work_struct *work);
90static void init_watchdog_work(struct work_struct *work);
91static DECLARE_DELAYED_WORK(dogwork_struct, pet_watchdog_work);
92static DECLARE_WORK(init_dogwork_struct, init_watchdog_work);
93
Jeff Ohlstein7e668552011-10-06 16:17:25 -070094static int msm_watchdog_suspend(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095{
Jeff Ohlstein7e668552011-10-06 16:17:25 -070096 if (!enable)
97 return 0;
98
Rohit Vaswani085a9332011-09-28 18:57:24 -070099 __raw_writel(1, msm_tmr0_base + WDT0_RST);
100 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101 mb();
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700102 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103}
104
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700105static int msm_watchdog_resume(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700107 if (!enable)
108 return 0;
109
110 __raw_writel(1, msm_tmr0_base + WDT0_EN);
111 __raw_writel(1, msm_tmr0_base + WDT0_RST);
112 mb();
113 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114}
115
116static int panic_wdog_handler(struct notifier_block *this,
117 unsigned long event, void *ptr)
118{
119 if (panic_timeout == 0) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700120 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122 } else {
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700123 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700124 msm_tmr0_base + WDT0_BARK_TIME);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700125 __raw_writel(WDT_HZ * (panic_timeout + 4),
Rohit Vaswani085a9332011-09-28 18:57:24 -0700126 msm_tmr0_base + WDT0_BITE_TIME);
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700127 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700128 }
129 return NOTIFY_DONE;
130}
131
132static struct notifier_block panic_blk = {
133 .notifier_call = panic_wdog_handler,
134};
135
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136static int wdog_enable_set(const char *val, struct kernel_param *kp)
137{
138 int ret = 0;
139 int old_val = runtime_disable;
140
141 mutex_lock(&disable_lock);
142
143 if (!enable) {
144 printk(KERN_INFO "MSM Watchdog is not active.\n");
145 ret = -EINVAL;
146 goto done;
147 }
148
149 ret = param_set_int(val, kp);
150
151 if (ret)
152 goto done;
153
154 switch (runtime_disable) {
155
156 case 1:
157 if (!old_val) {
Vikram Mulukutlaf29e8182011-09-30 14:07:13 -0700158 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
161 enable = 0;
162 atomic_notifier_chain_unregister(&panic_notifier_list,
163 &panic_blk);
164 cancel_delayed_work(&dogwork_struct);
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700165 /* may be suspended after the first write above */
166 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167 printk(KERN_INFO "MSM Watchdog deactivated.\n");
168 }
169 break;
170
171 default:
172 runtime_disable = old_val;
173 ret = -EINVAL;
174 break;
175
176 }
177
178done:
179 mutex_unlock(&disable_lock);
180 return ret;
181}
182
183void pet_watchdog(void)
184{
Rohit Vaswani085a9332011-09-28 18:57:24 -0700185 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186 last_pet = sched_clock();
187}
188
189static void pet_watchdog_work(struct work_struct *work)
190{
191 pet_watchdog();
192
193 if (enable)
194 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
195}
196
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700197static int msm_watchdog_remove(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198{
199 if (enable) {
Rohit Vaswani085a9332011-09-28 18:57:24 -0700200 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202 free_irq(WDT0_ACCSCSSNBARK_INT, 0);
203 enable = 0;
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700204 /* In case we got suspended mid-exit */
205 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 }
207 printk(KERN_INFO "MSM Watchdog Exit - Deactivated\n");
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700208 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209}
210
211static irqreturn_t wdog_bark_handler(int irq, void *dev_id)
212{
213 unsigned long nanosec_rem;
214 unsigned long long t = sched_clock();
215 struct task_struct *tsk;
216
217 nanosec_rem = do_div(t, 1000000000);
218 printk(KERN_INFO "Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t,
219 nanosec_rem / 1000);
220
221 nanosec_rem = do_div(last_pet, 1000000000);
222 printk(KERN_INFO "Watchdog last pet at %lu.%06lu\n", (unsigned long)
223 last_pet, nanosec_rem / 1000);
224
225 if (print_all_stacks) {
226
227 /* Suspend wdog until all stacks are printed */
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700228 msm_watchdog_suspend(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229
230 printk(KERN_INFO "Stack trace dump:\n");
231
232 for_each_process(tsk) {
233 printk(KERN_INFO "\nPID: %d, Name: %s\n",
234 tsk->pid, tsk->comm);
235 show_stack(tsk, NULL);
236 }
237
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700238 msm_watchdog_resume(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239 }
240
241 panic("Apps watchdog bark received!");
242 return IRQ_HANDLED;
243}
244
245#define SCM_SET_REGSAVE_CMD 0x2
246
Rohit Vaswani085a9332011-09-28 18:57:24 -0700247static void configure_bark_dump(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248{
249 int ret;
250 struct {
251 unsigned addr;
252 int len;
253 } cmd_buf;
254
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700255 if (!appsbark) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256 scm_regsave = (void *)__get_free_page(GFP_KERNEL);
257
258 if (scm_regsave) {
259 cmd_buf.addr = __pa(scm_regsave);
260 cmd_buf.len = PAGE_SIZE;
261
262 ret = scm_call(SCM_SVC_UTIL, SCM_SET_REGSAVE_CMD,
263 &cmd_buf, sizeof(cmd_buf), NULL, 0);
264 if (ret)
265 pr_err("Setting register save address failed.\n"
266 "Registers won't be dumped on a dog "
267 "bite\n");
Rohit Vaswani085a9332011-09-28 18:57:24 -0700268 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269 pr_err("Allocating register save space failed\n"
270 "Registers won't be dumped on a dog bite\n");
271 /*
272 * No need to bail if allocation fails. Simply don't
273 * send the command, and the secure side will reset
274 * without saving registers.
275 */
Rohit Vaswani085a9332011-09-28 18:57:24 -0700276 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700277 }
Rohit Vaswani085a9332011-09-28 18:57:24 -0700278}
279
280static void init_watchdog_work(struct work_struct *work)
281{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700282 u64 timeout = (bark_time * WDT_HZ)/1000;
283 __raw_writel(timeout, msm_tmr0_base + WDT0_BARK_TIME);
284 __raw_writel(timeout + 3*WDT_HZ, msm_tmr0_base + WDT0_BITE_TIME);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285
286 schedule_delayed_work_on(0, &dogwork_struct, delay_time);
287
288 atomic_notifier_chain_register(&panic_notifier_list,
289 &panic_blk);
290
Rohit Vaswani085a9332011-09-28 18:57:24 -0700291 __raw_writel(1, msm_tmr0_base + WDT0_EN);
292 __raw_writel(1, msm_tmr0_base + WDT0_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 last_pet = sched_clock();
294
295 printk(KERN_INFO "MSM Watchdog Initialized\n");
296
297 return;
298}
299
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700300static int msm_watchdog_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301{
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700302 struct msm_watchdog_pdata *pdata = pdev->dev.platform_data;
303 int ret;
304
305 if (!enable || !pdata || !pdata->pet_time || !pdata->bark_time) {
306 printk(KERN_INFO "MSM Watchdog Not Initialized\n");
307 return -ENODEV;
308 }
309
310 if (!pdata->has_secure)
311 appsbark = 1;
312
313 bark_time = pdata->bark_time;
314
315 msm_tmr0_base = msm_timer_get_timer0_base();
316
317 /* Must request irq before sending scm command */
318 ret = request_irq(WDT0_ACCSCSSNBARK_INT, wdog_bark_handler, 0,
319 "apps_wdog_bark", NULL);
320 if (ret)
321 return -EINVAL;
322
323 /*
324 * This is only temporary till SBLs turn on the XPUs
325 * This initialization will be done in SBLs on a later releases
326 */
327 if (cpu_is_msm9615())
328 __raw_writel(0xF, MSM_TCSR_BASE + TCSR_WDT_CFG);
329
330 configure_bark_dump();
331
332 delay_time = msecs_to_jiffies(pdata->pet_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700333 schedule_work_on(0, &init_dogwork_struct);
334 return 0;
335}
336
Jeff Ohlstein7e668552011-10-06 16:17:25 -0700337static const struct dev_pm_ops msm_watchdog_dev_pm_ops = {
338 .suspend_noirq = msm_watchdog_suspend,
339 .resume_noirq = msm_watchdog_resume,
340};
341
342static struct platform_driver msm_watchdog_driver = {
343 .probe = msm_watchdog_probe,
344 .remove = msm_watchdog_remove,
345 .driver = {
346 .name = MODULE_NAME,
347 .owner = THIS_MODULE,
348 .pm = &msm_watchdog_dev_pm_ops,
349 },
350};
351
352static int init_watchdog(void)
353{
354 return platform_driver_register(&msm_watchdog_driver);
355}
356
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357late_initcall(init_watchdog);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358MODULE_DESCRIPTION("MSM Watchdog Driver");
359MODULE_VERSION("1.0");
360MODULE_LICENSE("GPL v2");