2 * Watchdog driver for the SC8830
4 * Copyright (C) 2013 Spreadtrum
6 * based on sa1100_wdt.c and sc8830 arch reset implementation.
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
19 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21 #include <linux/module.h>
22 #include <linux/moduleparam.h>
23 #include <linux/types.h>
24 #include <linux/kernel.h>
25 #include <linux/module.h>
27 #include <linux/of_device.h>
29 #include <linux/device.h>
30 #include <linux/miscdevice.h>
31 #include <linux/watchdog.h>
32 #include <linux/init.h>
33 #include <linux/uaccess.h>
34 #include <linux/timex.h>
35 #include <linux/syscore_ops.h>
36 #include <linux/kthread.h>
37 #include <linux/interrupt.h>
38 #include <linux/sysrq.h>
39 #include <linux/delay.h>
40 #include <linux/irq.h>
41 #include <soc/sprd/sys_reset.h>
43 #include <asm/cacheflush.h>
44 #include <linux/platform_device.h>
45 #include "sprd_wdt_sys.h"
46 #ifdef CONFIG_SPRD_WATCHDOG_SYS_FIQ
47 #include <asm/fiq_glue.h>
50 #define KERNEL_ONLY_CHIP_DOG 0
51 #define KERNEL_WATCHDOG_FEEDER 1
53 #define FEED_CNT 10 /* (counts) Default is 3 */
54 #define FEED_PERIOD 3 /* (secs) Default is 3 sec */
55 #define CA7_IRQ_MARGIN (FEED_PERIOD * FEED_CNT) /* (secs) Default is 9 sec */
56 #define CA7_MARGIN (CA7_IRQ_MARGIN + 1) /* (secs) Default is 10 sec */
57 #define AP_MARGIN (FEED_PERIOD * (FEED_CNT + 1)) /* (secs) Default is 12 sec */
58 #if KERNEL_ONLY_CHIP_DOG
59 #define CHIP_MARGIN (CA7_IRQ_MARGIN - 1) /* (secs) Default is 8 sec */
61 #define CHIP_MARGIN (FEED_PERIOD * (FEED_CNT + 2)) /* (secs) Default is 15 sec */
64 static int feed_period = FEED_PERIOD;
65 static int ca7_irq_margin = CA7_IRQ_MARGIN;
66 static int ca7_margin = CA7_MARGIN;
67 static int ap_margin = AP_MARGIN;
68 static int chip_margin = CHIP_MARGIN;
69 static unsigned long wdt_enabled;
71 #define wdt_feed_all() FEED_ALL_WDG(chip_margin, ap_margin, ca7_margin, ca7_irq_margin)
73 extern int in_calibration(void);
74 #ifndef CONFIG_SPRD_WATCHDOG_SYS_FIQ
75 static irqreturn_t ca7_wdg_isr(int irq, void *dev_id)
77 pr_debug("%s\n", __func__);
78 pr_info("watchdog timeout interrupt happen\n");
79 sci_glb_set(AP_WDG_INT_CLR, WDG_INT_CLEAR_BIT);
82 pr_info("Current PID %d is %s\n", task_pid_nr(current), current->comm);
83 panic("watchdog timeout interrupt happen\n");
87 static void ca7_wdg_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp)
90 outer_disable(); /* l2x0_disable */
92 mdelay(50); /* wait for other application processor finish printk */
93 arch_trigger_all_cpu_backtrace();
94 panic("Hardware Watchdog interrupt barks on <cpu%d>\n", smp_processor_id());
95 __raw_writel(WDG_INT_CLEAR_BIT, CA7_WDG_INT_CLR);
97 static struct fiq_glue_handler ca7_wdg_fiq_glue_handler = {
102 #define sprd_wdt_shutdown NULL
104 static void sprd_wdt_resume(void)
106 pr_debug("%s\n", __FUNCTION__);
114 static int sprd_wdt_suspend(void)
116 pr_debug("%s\n", __FUNCTION__);
123 static struct syscore_ops sprd_wdt_syscore_ops = {
124 .shutdown = sprd_wdt_shutdown,
125 .suspend = sprd_wdt_suspend,
126 .resume = sprd_wdt_resume,
129 #if KERNEL_WATCHDOG_FEEDER
130 #define check_first_enter() \
132 static bool first = 1; \
138 #define check_first_enter()
141 static inline int wdt_init(void)
144 if (in_calibration()) {
145 pr_info("calibration mode, quit...\n");
151 /* 0xf0 coressponding to wachdog reboot mode of bootloader */
152 sci_adi_raw_write(ANA_RST_STATUS, 0xf0);
154 register_syscore_ops(&sprd_wdt_syscore_ops);
158 static inline int wdt_exit(void)
160 if (in_calibration()) {
161 pr_info("calibration mode, quit...\n");
165 unregister_syscore_ops(&sprd_wdt_syscore_ops);
169 #if KERNEL_WATCHDOG_FEEDER
170 #define KERNEL_WATCHDOG_FEEDER_BIT 0
171 static unsigned long enabled;
172 static struct task_struct *feed_task;
174 * This is a hw watchdog starts after init, and fed by a thread started
175 * by the watchdog driver itself. It can also be fed from userland.
176 * The watchdog will be stopped in system suspend, and restarted in
179 static int watchdog_feeder(void *data)
182 if (kthread_should_stop())
184 pr_debug("%s, ca7_margin=%d, ap_margain=%d, chip_margin=%d, feed_period=%d, wdt_enabled=%ld\n",
185 __FUNCTION__, ca7_margin, ap_margin, chip_margin, feed_period, wdt_enabled);
186 if (wdt_enabled & (1 << KERNEL_WATCHDOG_FEEDER_BIT))
188 set_current_state(TASK_INTERRUPTIBLE);
189 schedule_timeout(feed_period * HZ);
192 pr_info(KERN_ERR "watchdog_feeder exit\n");
196 static void wdt_start(void)
199 pr_info("watchdog enable\n");
200 watchdog_start(chip_margin, ap_margin, ca7_margin, ca7_irq_margin);
202 pr_info("watchdog disable\n");
207 static int __init sci_wdt_kfeeder_init(void)
212 feed_task = kthread_create(watchdog_feeder, NULL, "watchdog_feeder");
216 feed_task = kthread_create_on_node(watchdog_feeder, NULL, cpu_to_node(cpu), "watchdog_feeder/%d", cpu);
217 kthread_bind(feed_task, cpu);
220 if (feed_task == 0) {
221 pr_err("Can't crate watchdog_feeder thread!\n");
224 set_bit(KERNEL_WATCHDOG_FEEDER_BIT, &wdt_enabled);
228 wake_up_process(feed_task);
231 pr_info("SC8830 Watchdog: chip margin %d sec\n", chip_margin);
232 pr_info("SC8830 Watchdog: ap margin %d sec\n", ap_margin);
233 pr_info("SC8830 Watchdog: ca7 margin %d sec\n", ca7_margin);
234 pr_info("SC8830 Watchdog: ca7 irq margin %d sec\n", ca7_irq_margin);
239 static void __exit sci_wdt_kfeeder_exit(void)
242 kthread_stop(feed_task);
246 int param_set_enabled(const char *val, struct kernel_param *kp)
250 ret = param_set_ulong(val, kp);
254 set_bit(KERNEL_WATCHDOG_FEEDER_BIT, &wdt_enabled);
256 clear_bit(KERNEL_WATCHDOG_FEEDER_BIT, &wdt_enabled);
258 wake_up_process(feed_task);
262 int param_set_feed_period(const char *val, struct kernel_param *kp)
266 ret = param_set_int(val, kp);
269 wake_up_process(feed_task);
273 module_param_call(enabled, param_set_enabled, param_get_ulong,
274 &enabled, S_IWUSR | S_IRUGO);
275 MODULE_PARM_DESC(enabled, "Enable kernel watchdog task to feed dog");
276 module_param_call(feed_period, param_set_feed_period, param_get_int,
277 &feed_period, S_IWUSR | S_IRUGO);
278 MODULE_PARM_DESC(feed_period, "Watchdog feed period in seconds (default 3s)");
281 #define SPRD_WATCHDOG_MINOR (WATCHDOG_MINOR - 1)
282 static int boot_status = 0;
284 static int sci_open(struct inode *inode, struct file *file)
286 if (test_and_set_bit(1, &wdt_enabled))
289 pr_debug("%s, ca7_margin=%d, ca7_irq_margin=%d, ap_margain=%d, chip_margin=%d, feed_period=%d\n", __FUNCTION__,
290 ca7_margin, ca7_irq_margin, ap_margin, chip_margin, feed_period);
292 watchdog_start(chip_margin, ap_margin, ca7_margin, ca7_irq_margin);
294 return nonseekable_open(inode, file);
297 static int sci_release(struct inode *inode, struct file *file)
299 clear_bit(1, &wdt_enabled);
304 static ssize_t sci_write(struct file *file, const char __user *data, size_t len,
312 static const struct watchdog_info ident = {
313 .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
314 .identity = "SC8830 Watchdog",
315 .firmware_version = 1,
318 static long sci_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
322 void __user *argp = (void __user *) arg;
323 int __user *p = argp;
326 case WDIOC_GETSUPPORT:
327 ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
330 case WDIOC_GETSTATUS:
331 ret = put_user(0, p);
334 case WDIOC_GETBOOTSTATUS:
335 ret = put_user(boot_status, p);
338 case WDIOC_KEEPALIVE:
343 case WDIOC_SETTIMEOUT:
344 ret = get_user(time, p);
348 if (time <= 0 || (WDG_CLK * (long long) time >= 0xffffffff)) {
353 ca7_irq_margin = time;
356 case WDIOC_GETTIMEOUT:
357 ret = put_user(ca7_irq_margin, p);
363 static const struct file_operations sci_fops =
365 .owner = THIS_MODULE,
368 .unlocked_ioctl = sci_ioctl,
370 .release = sci_release,
373 static struct miscdevice sci_miscdev =
375 .minor = SPRD_WATCHDOG_MINOR,
376 .name = "sprd-watchdog",
380 static int sprd_wdt_probe(struct platform_device *pdev)
382 struct resource *ap_res,*a7_res;
386 ret = misc_register(&sci_miscdev);
389 misc_deregister(&sci_miscdev);
392 ap_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
395 dev_err(&pdev->dev, "sprd_wdt get base resource failed!\n");
398 ap_wdt_addr = (unsigned long)devm_ioremap_nocache(&pdev->dev, ap_res->start,resource_size(ap_res));
401 dev_err(&pdev->dev, "sprd_wdt get base address failed!\n");
405 a7_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
407 dev_err(&pdev->dev, "sprd_wdt get io resource failed!\n");
410 a7_wdt_addr = (unsigned long)devm_ioremap_nocache(&pdev->dev, a7_res->start,resource_size(a7_res));
413 dev_err(&pdev->dev, "sprd_wdt get base address failed!\n");
417 irq_num = platform_get_irq(pdev, 0);
419 dev_err(&pdev->dev, "no IRQ resource info\n");
422 #ifndef CONFIG_SPRD_WATCHDOG_SYS_FIQ
423 ret = devm_request_irq(&pdev->dev, irq_num, ca7_wdg_isr, IRQF_NO_SUSPEND , "sprd_wdg", NULL);
426 pr_info("sprd wdg isr register failed");
430 ret = fiq_glue_register_handler(&ca7_wdg_fiq_glue_handler);
434 pr_err("<%s> fiq_glue_register_handler failed %d!\n", __func__, ret);
437 sci_wdt_kfeeder_init();
444 static int __exit sprd_wdt_remove(struct platform_device *pdev)
446 misc_deregister(&sci_miscdev);
450 static const struct of_device_id sprd_wdt_match_table[] = {
451 { .compatible = "sprd,sprd-wdt", },
455 static struct platform_driver sprd_wdt_driver = {
456 .probe = sprd_wdt_probe,
457 .remove = __exit_p(sprd_wdt_remove),
460 .owner = THIS_MODULE,
461 .of_match_table = of_match_ptr(sprd_wdt_match_table),
466 static int __init sci_wdt_init(void)
474 pr_info("SC8830 Watchdog: userspace watchdog feeder\n");
476 platform_driver_register(&sprd_wdt_driver);
484 static void __exit sci_wdt_exit(void)
487 platform_driver_unregister(&sprd_wdt_driver);
490 module_init(sci_wdt_init);
491 module_exit(sci_wdt_exit);
493 MODULE_ALIAS_MISCDEV(SPRD_WATCHDOG_MINOR);
495 module_param(chip_margin, int, S_IWUSR | S_IRUGO);
496 MODULE_PARM_DESC(chip_margin, "Chip Watchdog margin in seconds (default 15s)");
497 module_param(ca7_margin, int, S_IWUSR | S_IRUGO);
498 MODULE_PARM_DESC(ca7_margin, "CA7 Watchdog margin in seconds (default 10s)");
499 module_param(ca7_irq_margin, int, S_IWUSR | S_IRUGO);
500 MODULE_PARM_DESC(ca7_irq_margin, "CA7 Watchdog irq margin in seconds (default 9s)");
501 module_param(ap_margin, int, S_IWUSR | S_IRUGO);
502 MODULE_PARM_DESC(ap_margin, "AP Watchdog margin in seconds (default 12s)");
504 MODULE_DESCRIPTION("SPRD Watchdog");
505 MODULE_LICENSE("GPL");