blob: 5e86e44bcd7ab911be71de93845015d1fa2a11ab [file] [log] [blame]
Neeraj Upadhyayd983a642018-08-20 14:22:48 +05301/* Copyright (c) 2018, The Linux Foundation. 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#include <linux/cpu.h>
14#include <linux/delay.h>
15#include <linux/io.h>
16#include <linux/interrupt.h>
17#include <linux/irq.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/of.h>
21#include <linux/kthread.h>
22#include <linux/platform_device.h>
23#include <linux/slab.h>
24#include <linux/wait.h>
25#include <linux/watchdog.h>
26#include <linux/of_device.h>
27
28#define MASK_SIZE 32
29#define QCOM_VM_WDT_HZ 32765
30#define QCOM_VM_PET_TIMEOUT 9U
31#define QCOM_VM_BARK_TIMEOUT 11U
32
33enum qcom_vm_wdt_reg {
34 WDT_RST,
35 WDT_CTRL,
36 WDT_STS,
37 WDT_BARK_TIME,
38 WDT_BITE_TIME,
39};
40
41static const u32 qcom_vm_wdt_reg_offset_data[] = {
42 [WDT_RST] = 0x4,
43 [WDT_CTRL] = 0x8,
44 [WDT_STS] = 0xC,
45 [WDT_BARK_TIME] = 0x10,
46 [WDT_BITE_TIME] = 0x14,
47};
48
49struct qcom_vm_wdt {
50 struct watchdog_device wdd;
51 void __iomem *base;
52 const u32 *layout;
53 unsigned int bark_irq;
54 unsigned int bite_irq;
55 unsigned int bark_time;
56 unsigned int pet_time;
57 unsigned long long last_pet;
58 struct device *dev;
59 struct notifier_block panic_blk;
60 struct timer_list pet_timer;
61 bool timer_expired;
62 wait_queue_head_t pet_complete;
63 struct task_struct *watchdog_task;
64 cpumask_t alive_mask;
65 struct mutex disable_lock;
66};
67
68static int enable = 1;
69module_param(enable, int, 0000);
70
71/* Disable the watchdog in hypervisor */
72static int hyp_enable = 1;
73module_param(hyp_enable, int, 0000);
74
75static void __iomem *qcom_vm_wdt_addr(struct qcom_vm_wdt *wdt,
76 enum qcom_vm_wdt_reg reg)
77{
78 return wdt->base + wdt->layout[reg];
79}
80
81static void dump_cpu_alive_mask(struct qcom_vm_wdt *wdt)
82{
83 static char alive_mask_buf[MASK_SIZE];
84
85 scnprintf(alive_mask_buf, MASK_SIZE, "%*pb1", cpumask_pr_args(
86 &wdt->alive_mask));
87 dev_info(wdt->dev, "cpu alive mask from last pet %s\n",
88 alive_mask_buf);
89}
90
91static irqreturn_t qcom_vm_wdt_bark_handler(int irq, void *dev_id)
92{
93 struct qcom_vm_wdt *wdt = (struct qcom_vm_wdt *)dev_id;
94 unsigned long long t = sched_clock();
95
96 dev_info(wdt->dev, "Watchdog bark! Now = %lu\n",
97 (unsigned long) t);
98 dev_info(wdt->dev, "Watchdog last pet at %lu\n",
99 (unsigned long) wdt->last_pet);
100 dump_cpu_alive_mask(wdt);
101 pr_info("Causing a watchdog bite!");
102 __raw_writel(1 * QCOM_VM_WDT_HZ, qcom_vm_wdt_addr(wdt, WDT_BITE_TIME));
103 /* Make sure bite time is written before we reset */
104 mb();
105 __raw_writel(1, qcom_vm_wdt_addr(wdt, WDT_RST));
106 /* Make sure we wait only after reset */
107 mb();
108 /* Delay to make sure bite occurs */
109 msleep(10000);
110
111 panic("Failed to cause a watchdog bite! - Falling back to kernel panic!");
112 return IRQ_HANDLED;
113}
114
115static int qcom_vm_wdt_suspend(struct device *dev)
116{
117 struct qcom_vm_wdt *wdt =
118 (struct qcom_vm_wdt *)dev_get_drvdata(dev);
119
120 if (!enable)
121 return 0;
122
123 __raw_writel(1, qcom_vm_wdt_addr(wdt, WDT_RST));
124 /* Make sure watchdog is suspended before setting enable */
125 mb();
126
127 wdt->last_pet = sched_clock();
128 return 0;
129}
130
131static int qcom_vm_wdt_resume(struct device *dev)
132{
133 struct qcom_vm_wdt *wdt =
134 (struct qcom_vm_wdt *)dev_get_drvdata(dev);
135
136 if (!enable)
137 return 0;
138
139 __raw_writel(1, qcom_vm_wdt_addr(wdt, WDT_RST));
140 /* Make sure watchdog is suspended before setting enable */
141 mb();
142
143 wdt->last_pet = sched_clock();
144 return 0;
145}
146
147static int qcom_vm_wdt_panic_handler(struct notifier_block *this,
148 unsigned long event, void *ptr)
149{
150 struct qcom_vm_wdt *wdt = container_of(this, struct qcom_vm_wdt,
151 panic_blk);
152
153 __raw_writel(QCOM_VM_WDT_HZ * (panic_timeout + 10),
154 qcom_vm_wdt_addr(wdt, WDT_BARK_TIME));
155 __raw_writel(QCOM_VM_WDT_HZ * (panic_timeout + 10),
156 qcom_vm_wdt_addr(wdt, WDT_BITE_TIME));
157 __raw_writel(1, qcom_vm_wdt_addr(wdt, WDT_RST));
158 /*
159 * Ensure that bark, bite times, and reset is done, before
160 * moving forward.
161 */
162 mb();
163
164 return NOTIFY_DONE;
165}
166
167static void qcom_vm_wdt_disable(struct qcom_vm_wdt *wdt)
168{
169 if (hyp_enable == 1)
170 __raw_writel(0, qcom_vm_wdt_addr(wdt, WDT_CTRL));
171 /* Make sure watchdog is disabled before proceeding */
172 mb();
173 devm_free_irq(wdt->dev, wdt->bark_irq, wdt);
174 enable = 0;
175 /*Ensure all cpus see update to enable*/
176 smp_mb();
177 atomic_notifier_chain_unregister(&panic_notifier_list,
178 &wdt->panic_blk);
179 del_timer_sync(&wdt->pet_timer);
180 /* may be suspended after the first write above */
181 if (hyp_enable == 1)
182 __raw_writel(0, qcom_vm_wdt_addr(wdt, WDT_CTRL));
183 /* Make sure watchdog is disabled before setting enable */
184 mb();
185 pr_info("QCOM VM Watchdog deactivated.\n");
186}
187
188static ssize_t qcom_vm_wdt_disable_get(struct device *dev,
189 struct device_attribute *attr, char *buf)
190{
191 int ret;
192 struct qcom_vm_wdt *wdt = dev_get_drvdata(dev);
193
194 mutex_lock(&wdt->disable_lock);
195 ret = snprintf(buf, PAGE_SIZE, "%d\n", enable == 0 ? 1 : 0);
196 mutex_unlock(&wdt->disable_lock);
197 return ret;
198}
199
200static ssize_t qcom_vm_wdt_disable_set(struct device *dev,
201 struct device_attribute *attr,
202 const char *buf, size_t count)
203{
204 int ret;
205 u8 disable;
206 struct qcom_vm_wdt *wdt = dev_get_drvdata(dev);
207
208 ret = kstrtou8(buf, 10, &disable);
209 if (ret) {
210 dev_err(wdt->dev, "invalid user input\n");
211 return ret;
212 }
213 if (disable == 1) {
214 mutex_lock(&wdt->disable_lock);
215 if (enable == 0) {
216 pr_info("QCOM VM Watchdog already disabled\n");
217 mutex_unlock(&wdt->disable_lock);
218 return count;
219 }
220 qcom_vm_wdt_disable(wdt);
221 mutex_unlock(&wdt->disable_lock);
222 } else {
223 pr_err("invalid operation, only disable = 1 supported\n");
224 return -EINVAL;
225 }
226 return count;
227}
228
229static DEVICE_ATTR(disable, 0600, qcom_vm_wdt_disable_get,
230 qcom_vm_wdt_disable_set);
231
232static void qcom_vm_wdt_hyp_disable(struct qcom_vm_wdt *wdt)
233{
234 __raw_writel(1 << 2, qcom_vm_wdt_addr(wdt, WDT_CTRL));
235 hyp_enable = 0;
236}
237
238static ssize_t qcom_vm_wdt_hyp_disable_get(struct device *dev,
239 struct device_attribute *attr, char *buf)
240{
241 int ret;
242 struct qcom_vm_wdt *wdt = dev_get_drvdata(dev);
243
244 mutex_lock(&wdt->disable_lock);
245 ret = snprintf(buf, PAGE_SIZE, "%d\n", hyp_enable == 0 ? 1 : 0);
246 mutex_unlock(&wdt->disable_lock);
247 return ret;
248}
249
250static ssize_t qcom_vm_wdt_hyp_disable_set(struct device *dev,
251 struct device_attribute *attr,
252 const char *buf, size_t count)
253{
254 int ret;
255 u8 disable;
256 struct qcom_vm_wdt *wdt = dev_get_drvdata(dev);
257
258 ret = kstrtou8(buf, 10, &disable);
259 if (ret) {
260 dev_err(wdt->dev, "invalid user input\n");
261 return ret;
262 }
263 if (disable == 1) {
264 mutex_lock(&wdt->disable_lock);
265 if (hyp_enable == 0) {
266 pr_info("QCOM VM Watchdog already disabled in Hyp\n");
267 mutex_unlock(&wdt->disable_lock);
268 return count;
269 }
270 qcom_vm_wdt_hyp_disable(wdt);
271 mutex_unlock(&wdt->disable_lock);
272 } else {
273 pr_err("invalid operation, only hyp_disable = 1 supported\n");
274 return -EINVAL;
275 }
276 return count;
277}
278
279static DEVICE_ATTR(hyp_disable, 0600, qcom_vm_wdt_hyp_disable_get,
280 qcom_vm_wdt_hyp_disable_set);
281
282static int qcom_vm_wdt_start(struct qcom_vm_wdt *wdt)
283{
284 __raw_writel(0, qcom_vm_wdt_addr(wdt, WDT_CTRL));
285 __raw_writel(1, qcom_vm_wdt_addr(wdt, WDT_RST));
286 __raw_writel((wdt->bark_time / 1000) * QCOM_VM_WDT_HZ,
287 qcom_vm_wdt_addr(wdt, WDT_BARK_TIME));
288 __raw_writel((wdt->bark_time / 1000 + 3) * QCOM_VM_WDT_HZ,
289 qcom_vm_wdt_addr(wdt, WDT_BITE_TIME));
290 __raw_writel(1, qcom_vm_wdt_addr(wdt, WDT_CTRL));
291 return 0;
292}
293
294static void keep_alive_response(void *info)
295{
296 int cpu = smp_processor_id();
297 struct qcom_vm_wdt *wdt = (struct qcom_vm_wdt *)info;
298
299 cpumask_set_cpu(cpu, &wdt->alive_mask);
300 /* Make sure alive mask is cleared and set in order */
301 smp_mb();
302}
303
304static int qcom_vm_wdt_ping(struct qcom_vm_wdt *wdt)
305{
306 int cpu;
307
308 cpumask_clear(&wdt->alive_mask);
309 for_each_cpu(cpu, cpu_online_mask) {
310 smp_call_function_single(cpu, keep_alive_response, wdt, 1);
311 }
312 __raw_writel(1, qcom_vm_wdt_addr(wdt, WDT_RST));
313 wdt->last_pet = sched_clock();
314 return 0;
315}
316
317static void vm_pet_task_wakeup(unsigned long data)
318{
319 struct qcom_vm_wdt *wdt = (struct qcom_vm_wdt *)data;
320
321 wdt->timer_expired = true;
322 wake_up(&wdt->pet_complete);
323}
324
325static __ref int vm_watchdog_kthread(void *arg)
326{
327 struct qcom_vm_wdt *wdt = (struct qcom_vm_wdt *)arg;
328
329 while (!kthread_should_stop()) {
330 while (wait_event_interruptible(
331 wdt->pet_complete,
332 wdt->timer_expired) != 0)
333 ;
334 if (enable)
335 qcom_vm_wdt_ping(wdt);
336 wdt->timer_expired = false;
337 mod_timer(&wdt->pet_timer, jiffies +
338 msecs_to_jiffies(wdt->pet_time));
339 }
340 return 0;
341}
342
343static int qcom_vm_wdt_probe(struct platform_device *pdev)
344{
345 struct qcom_vm_wdt *wdt;
346 struct resource *res;
347 const u32 *regs;
348 int ret, error;
349
350 if (!pdev->dev.of_node || !enable)
351 return -ENODEV;
352
353 regs = of_device_get_match_data(&pdev->dev);
354 if (!regs) {
355 dev_err(&pdev->dev, "Unsupported QCOM WDT module\n");
356 return -ENODEV;
357 }
358
359 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
360 if (!wdt)
361 return -ENOMEM;
362
363 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wdt-base");
364 if (!res) {
365 ret = -ENODEV;
366 goto err;
367 }
368
369 wdt->base = devm_ioremap_resource(&pdev->dev, res);
370 if (IS_ERR(wdt->base)) {
371 ret = PTR_ERR(wdt->base);
372 goto err;
373 }
374
375 wdt->layout = regs;
376 wdt->dev = &pdev->dev;
377
378 wdt->bark_irq = platform_get_irq(pdev, 0);
379 wdt->bite_irq = platform_get_irq(pdev, 1);
380 wdt->last_pet = sched_clock();
381
382 ret = of_property_read_u32(pdev->dev.of_node, "qcom,bark-time",
383 &wdt->bark_time);
384 if (ret)
385 wdt->bark_time = QCOM_VM_BARK_TIMEOUT * 1000;
386 ret = of_property_read_u32(pdev->dev.of_node, "qcom,pet-time",
387 &wdt->pet_time);
388 if (ret)
389 wdt->pet_time = QCOM_VM_PET_TIMEOUT * 1000;
390
391 wdt->watchdog_task = kthread_create(vm_watchdog_kthread, wdt,
392 "qcom_vm_watchdog");
393 if (IS_ERR(wdt->watchdog_task)) {
394 ret = PTR_ERR(wdt->watchdog_task);
395 goto err;
396 }
397
398 init_waitqueue_head(&wdt->pet_complete);
399 wdt->timer_expired = false;
400 wake_up_process(wdt->watchdog_task);
401 init_timer(&wdt->pet_timer);
402 wdt->pet_timer.data = (unsigned long)wdt;
403 wdt->pet_timer.function = vm_pet_task_wakeup;
404 wdt->pet_timer.expires = jiffies +
405 msecs_to_jiffies(wdt->pet_time);
406 add_timer(&wdt->pet_timer);
407 cpumask_clear(&wdt->alive_mask);
408
409 wdt->panic_blk.notifier_call = qcom_vm_wdt_panic_handler;
410 atomic_notifier_chain_register(&panic_notifier_list, &wdt->panic_blk);
411
412 platform_set_drvdata(pdev, wdt);
413 mutex_init(&wdt->disable_lock);
414
415 error = device_create_file(wdt->dev, &dev_attr_disable);
416 error |= device_create_file(wdt->dev, &dev_attr_hyp_disable);
417
418 if (error)
419 dev_err(wdt->dev, "cannot create sysfs attribute\n");
420
421 qcom_vm_wdt_start(wdt);
422 ret = devm_request_irq(&pdev->dev, wdt->bark_irq,
423 qcom_vm_wdt_bark_handler, IRQF_TRIGGER_RISING,
424 "apps_wdog_bark", wdt);
425
426 if (hyp_enable == 0)
427 qcom_vm_wdt_hyp_disable(wdt);
428 return 0;
429
430err:
431 kfree(wdt);
432 return ret;
433}
434
435static int qcom_vm_wdt_remove(struct platform_device *pdev)
436{
437 struct qcom_vm_wdt *wdt = platform_get_drvdata(pdev);
438
439 mutex_lock(&wdt->disable_lock);
440 if (enable)
441 qcom_vm_wdt_disable(wdt);
442 mutex_unlock(&wdt->disable_lock);
443 device_remove_file(wdt->dev, &dev_attr_disable);
444 device_remove_file(wdt->dev, &dev_attr_hyp_disable);
445 dev_info(wdt->dev, "QCOM VM Watchdog Exit - Deactivated\n");
446 del_timer_sync(&wdt->pet_timer);
447 kthread_stop(wdt->watchdog_task);
448 kfree(wdt);
449 return 0;
450}
451
452static const struct of_device_id qcom_vm_wdt_of_table[] = {
453 { .compatible = "qcom,vm-wdt", .data = qcom_vm_wdt_reg_offset_data },
454 { },
455};
456MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
457
458static const struct dev_pm_ops qcom_vm_wdt_dev_pm_ops = {
459 .suspend_noirq = qcom_vm_wdt_suspend,
460 .resume_noirq = qcom_vm_wdt_resume,
461};
462
463static struct platform_driver qcom_vm_watchdog_driver = {
464 .probe = qcom_vm_wdt_probe,
465 .remove = qcom_vm_wdt_remove,
466 .driver = {
467 .name = KBUILD_MODNAME,
468 .pm = &qcom_vm_wdt_dev_pm_ops,
469 .of_match_table = qcom_vm_wdt_of_table,
470 },
471};
472module_platform_driver(qcom_vm_watchdog_driver);
473
474MODULE_DESCRIPTION("QCOM VM Watchdog Driver");
475MODULE_LICENSE("GPL v2");