1 /* MOXART Watchdog driver (based on MOXA sources)
2 * Copyright (C) 2013 Jonas Jensen <jonas.jensen@gmail.com>
3 * This program is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU General Public License as published by the
5 * Free Software Foundation; either version 2 of the License,
6 * or (at your option) any later version. */
8 #include <linux/module.h>
9 #include <linux/proc_fs.h>
10 #include <linux/string.h>
11 #include <linux/watchdog.h>
12 #include <linux/reboot.h>
13 #include <linux/miscdevice.h>
14 #include <linux/poll.h>
15 #include <linux/timer.h>
16 #include <linux/init.h>
17 #include <linux/interrupt.h>
18 #include <linux/uaccess.h>
21 #include <mach/hardware.h>
23 #define MOXA_WATCHDOG_MINOR WATCHDOG_MINOR
26 #define DEFAULT_WATCHDOG_TIME (30UL*1000UL)
29 #define WATCHDOG_MIN_TIME 50UL
32 #define WATCHDOG_MAX_TIME (60UL*1000UL)
34 /* 500 msec, for watchdog timer polling */
35 #define WATCHDOG_TOL_TIME (500UL)
37 /* 1 seconds, for watchdog timer count */
38 #define WATCHDOG_TOL_COUNT_TIME (1000UL)
40 #define WATCHDOG_COUNTER(x) ((APB_CLK/1000UL)*(x))
41 #define WATCHDOG_JIFFIES(x) ((((x)+WATCHDOG_TOL_TIME)*HZ)/1000UL)
43 /* enable watch dog and set time (unint msec) */
44 #define IOCTL_WATCHDOG_ENABLE 1
46 /* disable watch dog, kernle do it */
47 #define IOCTL_WATCHDOG_DISABLE 2
49 /* get now setting about mode and time */
50 #define IOCTL_WATCHDOG_GET_SETTING 3
52 /* to ack watch dog */
53 #define IOCTL_WATCHDOG_ACK 4
55 struct wdt_set_struct
{
60 static int opencounts
;
61 static int wdt_user_enabled
;
62 static unsigned long wdt_time
= DEFAULT_WATCHDOG_TIME
;
63 static struct timer_list wdt_timer
;
64 static struct work_struct rebootqueue
;
66 static void wdt_enable(void)
68 __raw_writel(WATCHDOG_COUNTER(wdt_time
+ WATCHDOG_TOL_COUNT_TIME
),
69 IO_ADDRESS(MOXART_WATCHDOG_BASE
) + 4);
70 __raw_writel(0x5ab9, IO_ADDRESS(MOXART_WATCHDOG_BASE
) + 8);
71 __raw_writel(0x03, IO_ADDRESS(MOXART_WATCHDOG_BASE
) + 12);
74 static void wdt_disable(void)
76 __raw_writel(0, IO_ADDRESS(MOXART_WATCHDOG_BASE
) + 12);
79 static int wdt_ioctl(struct inode
*inode
, struct file
*file
,
80 unsigned int cmd
, unsigned long arg
)
83 struct wdt_set_struct nowset
;
87 case IOCTL_WATCHDOG_ENABLE
:
88 if (copy_from_user(&time
, (void *) arg
, sizeof(time
)))
90 if (time
< WATCHDOG_MIN_TIME
|| time
> WATCHDOG_MAX_TIME
)
92 local_irq_save(flags
);
93 if (wdt_user_enabled
) {
95 del_timer(&wdt_timer
);
99 wdt_timer
.expires
= jiffies
+ WATCHDOG_JIFFIES(wdt_time
);
100 add_timer(&wdt_timer
);
102 local_irq_restore(flags
);
104 case IOCTL_WATCHDOG_DISABLE
:
105 local_irq_save(flags
);
106 if (wdt_user_enabled
) {
108 del_timer(&wdt_timer
);
109 wdt_user_enabled
= 0;
111 local_irq_restore(flags
);
113 case IOCTL_WATCHDOG_GET_SETTING
:
114 nowset
.mode
= wdt_user_enabled
;
115 nowset
.time
= wdt_time
;
116 if (copy_to_user((void *) arg
, &nowset
, sizeof(nowset
)))
119 case IOCTL_WATCHDOG_ACK
:
120 local_irq_save(flags
);
121 if (wdt_user_enabled
) {
123 del_timer(&wdt_timer
);
124 wdt_timer
.expires
= jiffies
+
125 WATCHDOG_JIFFIES(wdt_time
);
126 add_timer(&wdt_timer
);
129 local_irq_restore(flags
);
137 static int wdt_open(struct inode
*inode
, struct file
*file
)
141 dbg_printk(KERN_INFO
"MOXART watchdog: wdt_open\n");
142 if (MINOR(inode
->i_rdev
) != MOXA_WATCHDOG_MINOR
)
144 local_irq_save(flags
);
146 local_irq_restore(flags
);
150 static int wdt_release(struct inode
*inode
, struct file
*file
)
154 local_irq_save(flags
);
155 dbg_printk(KERN_INFO
"MOXART watchdog: wdt_release\n");
157 if (opencounts
<= 0) {
158 if (wdt_user_enabled
) {
160 del_timer(&wdt_timer
);
161 wdt_user_enabled
= 0;
165 local_irq_restore(flags
);
169 static void wdt_reboot(struct work_struct
*work
)
171 char *argv
[2], *envp
[5];
173 dbg_printk(KERN_INFO
"MOXART watchdog: wdt_reboot\n");
175 /* With this define set, watchdog reset can be verified.
176 * At boot: 10 second wdt_timer calls wdt_poll and after another
177 * 10 seconds a reset is forced. However, in this driver, a user
178 * mode reboot is preferred (but hardware watchdog should still work)
179 * (unless lockup happens after wdt_disable()) */
180 /* #define DEBUG_TEST_WATCHDOG */
181 #ifdef DEBUG_TEST_WATCHDOG
183 __raw_writel(APB_CLK*10, IO_ADDRESS(MOXART_WATCHDOG_BASE) + 4);
184 __raw_writel(0x5ab9, IO_ADDRESS(MOXART_WATCHDOG_BASE) + 8);
185 __raw_writel(0x03, IO_ADDRESS(MOXART_WATCHDOG_BASE) + 12);
191 /* if (!current->fs->root) return; */
192 argv
[0] = "/bin/reboot";
195 envp
[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
197 call_usermodehelper(argv
[0], argv
, envp
, 0);
200 static void wdt_poll(unsigned long ignore
)
204 local_irq_save(flags
);
206 #ifdef DEBUG_TEST_WATCHDOG
209 del_timer(&wdt_timer
);
210 pr_info("MOXART watchdog: Now reboot the system.\n");
211 schedule_work(&rebootqueue
);
212 local_irq_restore(flags
);
215 static int wdt_proc_output(char *buf
)
220 p
+= sprintf(p
, "user enable\t: %d\nack time\t: %d msec\n",
221 wdt_user_enabled
, (int) wdt_time
);
225 static int wdt_read_proc(char *page
, char **start
, off_t off
,
226 int count
, int *eof
, void *data
)
228 int len
= wdt_proc_output(page
);
230 if (len
<= off
+ count
)
241 static const struct file_operations moxart_wdt_fops
= {
242 .owner
= THIS_MODULE
,
246 .release
= wdt_release
,
249 static struct miscdevice wdt_dev
= {
255 static void __exit
moxart_wdt_exit(void)
259 local_irq_save(flags
);
260 if (wdt_user_enabled
) {
262 del_timer(&wdt_timer
);
263 wdt_user_enabled
= 0;
266 local_irq_restore(flags
);
267 misc_deregister(&wdt_dev
);
270 static int __init
moxart_wdt_init(void)
272 if (misc_register(&wdt_dev
)) {
273 pr_info("MOXART watchdog: misc_register failed!\n");
274 goto moxart_wdt_init_err
;
278 wdt_user_enabled
= 0;
280 INIT_WORK(&rebootqueue
, wdt_reboot
);
281 init_timer(&wdt_timer
);
282 wdt_timer
.function
= wdt_poll
;
284 #ifdef DEBUG_TEST_WATCHDOG
286 wdt_user_enabled
= 1;
287 wdt_timer
.expires
= jiffies
+ WATCHDOG_JIFFIES(wdt_time
);
288 add_timer(&wdt_timer
);
292 create_proc_read_entry("driver/moxart_wdt", 0, 0, wdt_read_proc
, NULL
);
294 pr_info("MOXART watchdog driver\n");
299 misc_deregister(&wdt_dev
);
303 module_init(moxart_wdt_init
);
304 module_exit(moxart_wdt_exit
);
306 MODULE_DESCRIPTION("MOXART watchdog driver");
307 MODULE_LICENSE("GPL");