3 * Date Aurhor Comment
\r
4 * 02-23-2006 Victor Yu. Create it.
\r
6 //#define __KERNEL_SYSCALLS__
\r
7 #include <linux/config.h>
\r
8 #include <linux/proc_fs.h>
\r
9 #include <linux/devfs_fs_kernel.h>
\r
10 #include <linux/unistd.h>
\r
11 #include <linux/string.h>
\r
12 #include <linux/ctype.h>
\r
13 #include <linux/module.h>
\r
14 #include <linux/kernel.h>
\r
15 #include <linux/miscdevice.h>
\r
16 #include <linux/fcntl.h>
\r
17 #include <linux/init.h>
\r
18 #include <linux/poll.h>
\r
19 #include <linux/spinlock.h>
\r
20 #include <linux/delay.h>
\r
21 #include <linux/timer.h>
\r
22 #include <linux/ioport.h>
\r
23 #include <linux/interrupt.h>
\r
24 #include <linux/sched.h>
\r
25 #include <linux/signal.h>
\r
26 #include <linux/mm.h>
\r
27 #include <linux/kmod.h>
\r
29 #include <asm/uaccess.h>
\r
30 #include <asm/system.h>
\r
31 #include <asm/irq.h>
\r
32 #include <asm/bitops.h>
\r
34 //#define VICTOR_DEBUG
\r
36 #define dbg_printk(x...) printk(x)
\r
38 #define dbg_printk(x...)
\r
41 #define MOXA_WATCHDOG_VERSION "1.0"
\r
42 #define MOXA_WATCHDOG_MINOR 106
\r
43 #define DEFAULT_WATCHDOG_TIME (30UL*1000UL) // 30 seconds
\r
44 #define WATCHDOG_MIN_TIME 50UL // 50 msec
\r
45 #define WATCHDOG_MAX_TIME (60UL*1000UL) // 60 seconds
\r
46 #define WATCHDOG_TOL_TIME (500UL) // 500 msec, for watchdog timer polling
\r
47 #define WATCHDOG_TOL_COUNT_TIME (1000UL) // 1 seconds, for watchdog timer count
\r
48 #define WATCHDOG_COUNTER(x) ((APB_CLK/1000UL)*(x))
\r
49 #define WATCHDOG_JIFFIES(x) ((((x)+WATCHDOG_TOL_TIME)*HZ)/1000UL)
\r
51 #if 0 // mask by Victor Yu. 05-15-2006
\r
52 static spinlock_t swtdlock=SPIN_LOCK_UNLOCKED;
\r
54 static int opencounts=0;
\r
55 static int swtduserenabled=0;
\r
56 static unsigned long swtdtime=DEFAULT_WATCHDOG_TIME;
\r
57 static struct timer_list swtdtimer;
\r
58 static struct work_struct rebootqueue;
\r
60 #if 1 // add by Jared Wu. 03-10-2009 Ack the watchdog instead of disable it.
\r
61 // this means that if the kernel crashed, the hardware watchdog will
\r
63 static void swtd_enable_with_time(unsigned long swtd_time)
\r
65 *(unsigned int *)(CPE_WATCHDOG_VA_BASE+4) = WATCHDOG_COUNTER(swtd_time+WATCHDOG_TOL_COUNT_TIME);
\r
66 *(unsigned int *)(CPE_WATCHDOG_VA_BASE+8) = 0x5ab9;
\r
67 *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0x03;
\r
72 static void swtd_enable(void)
\r
74 *(unsigned int *)(CPE_WATCHDOG_VA_BASE+4) = WATCHDOG_COUNTER(swtdtime+WATCHDOG_TOL_COUNT_TIME);
\r
75 *(unsigned int *)(CPE_WATCHDOG_VA_BASE+8) = 0x5ab9;
\r
76 *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0x03;
\r
79 static void swtd_disable(void)
\r
81 *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0;
\r
84 #define IOCTL_WATCHDOG_ENABLE 1 // enable watch dog and set time (unint msec)
\r
85 #define IOCTL_WATCHDOG_DISABLE 2 // disable watch dog, kernle do it
\r
86 #define IOCTL_WATCHDOG_GET_SETTING 3 // get now setting about mode and time
\r
87 #define IOCTL_WATCHDOG_ACK 4 // to ack watch dog
\r
88 struct swtd_set_struct {
\r
92 static int swtd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
\r
95 struct swtd_set_struct nowset;
\r
96 unsigned long flags;
\r
99 case IOCTL_WATCHDOG_ENABLE :
\r
100 if ( copy_from_user(&time, (void *)arg, sizeof(time)) )
\r
102 if ( time < WATCHDOG_MIN_TIME || time > WATCHDOG_MAX_TIME )
\r
104 #if 0 // mask by Victor Yu. 05-15-2006
\r
105 spin_lock(&swtdlock);
\r
106 del_timer(&swtdtimer);
\r
109 swtduserenabled = 1;
\r
110 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
\r
111 add_timer(&swtdtimer);
\r
113 spin_unlock(&swtdlock);
\r
114 #else // add by Victor Yu. 05-15-2006
\r
117 if ( swtduserenabled ) {
\r
119 del_timer(&swtdtimer);
\r
122 swtduserenabled = 1;
\r
123 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
\r
124 add_timer(&swtdtimer);
\r
126 restore_flags(flags);
\r
129 case IOCTL_WATCHDOG_DISABLE :
\r
130 #if 0 // mask by Victor Yu. 05-15-2006
\r
131 spin_lock(&swtdlock);
\r
132 if ( swtduserenabled ) {
\r
134 del_timer(&swtdtimer);
\r
135 swtduserenabled = 0;
\r
136 swtdtime = DEFAULT_WATCHDOG_TIME;
\r
137 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
\r
138 add_timer(&swtdtimer);
\r
141 spin_unlock(&swtdlock);
\r
142 #else // add by Victor Yu. 05-15-2006
\r
145 if ( swtduserenabled ) {
\r
147 del_timer(&swtdtimer);
\r
148 swtduserenabled = 0;
\r
150 restore_flags(flags);
\r
153 case IOCTL_WATCHDOG_GET_SETTING :
\r
154 nowset.mode = swtduserenabled;
\r
155 nowset.time = swtdtime;
\r
156 if ( copy_to_user((void *)arg, &nowset, sizeof(nowset)) )
\r
159 case IOCTL_WATCHDOG_ACK :
\r
160 #if 0 // mask by Victor Yu. 05-15-2006
\r
161 spin_lock(&swtdlock);
\r
162 if ( swtduserenabled ) {
\r
164 del_timer(&swtdtimer);
\r
165 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
\r
166 add_timer(&swtdtimer);
\r
169 spin_unlock(&swtdlock);
\r
170 #else // add by Victor Yu. 05-15-2006
\r
173 if ( swtduserenabled ) {
\r
175 del_timer(&swtdtimer);
\r
176 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
\r
177 add_timer(&swtdtimer);
\r
180 restore_flags(flags);
\r
189 static int swtd_open(struct inode *inode, struct file *file)
\r
191 unsigned long flags;
\r
193 if ( MINOR(inode->i_rdev) != MOXA_WATCHDOG_MINOR )
\r
195 #if 0 // mask by Victor Yu. 05-15-2006
\r
196 spin_lock(&swtdlock);
\r
198 spin_unlock(&swtdlock);
\r
199 #else // add by Victor Yu. 05-15-2006
\r
202 #if 0 // mask by Victor Yu. 05-15-2006
\r
203 if ( swtduserenabled == 0 ) {
\r
204 swtduserenabled = 1;
\r
205 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
\r
206 add_timer(&swtdtimer);
\r
211 restore_flags(flags);
\r
216 static int swtd_release(struct inode *inode, struct file *file)
\r
218 unsigned long flags;
\r
220 #if 0 // mask by Victor Yu. 05-15-2006
\r
221 spin_lock(&swtdlock);
\r
222 #else // add by Victor Yu. 05-15-2006
\r
226 dbg_printk("swtd_release entry.\n");
\r
228 if ( opencounts <= 0 ) {
\r
229 #if 0 // mask by Victor Yu. 02-23-2006
\r
230 if ( signal_pending(current) ) {
\r
231 dbg_printk("swtd_release has signal pending\n");
\r
232 dbg_printk("parent signal blocked=0x%x,0x%x, pending=0x%x,0x%x\n", current->parent->blocked.sig[0], current->parent->blocked.sig[1], current->parent->pending.signal.sig[0], current->parent->pending.signal.sig[1]);
\r
233 dbg_printk("signal blocked=0x%x,0x%x, pending=0x%x,0x%x\n", current->blocked.sig[0], current->blocked.sig[1], current->pending.signal.sig[0], current->pending.signal.sig[1]);
\r
234 if ( sigismember(¤t->parent->blocked, SIGKILL) ) {
\r
235 dbg_printk("swtd_release get SIGKILL signal\n");
\r
236 goto swtd_release_l1;
\r
238 if ( sigismember(¤t->blocked, SIGKILL) ) {
\r
239 dbg_printk("swtd_release get SIGKILL signal\n");
\r
240 goto swtd_release_l1;
\r
242 if ( sigismember(¤t->parent->blocked, SIGCHLD) ) {
\r
243 dbg_printk("swtd_release get SIGCHLD signal\n");
\r
244 goto swtd_release_l1;
\r
246 if ( sigismember(¤t->blocked, SIGCHLD) ) {
\r
247 dbg_printk("swtd_release get SIGCHLD signal\n");
\r
248 goto swtd_release_l1;
\r
250 if ( sigismember(¤t->parent->blocked, SIGTERM) ) {
\r
251 dbg_printk("swtd_release get SIGTERM signal\n");
\r
252 goto swtd_release_l1;
\r
254 if ( sigismember(¤t->blocked, SIGTERM) ) {
\r
255 dbg_printk("swtd_release get SIGTERM signal\n");
\r
256 goto swtd_release_l1;
\r
258 if ( sigismember(¤t->parent->blocked, SIGINT) ) {
\r
259 dbg_printk("swtd_release get SIGINT signal\n");
\r
260 goto swtd_release_l1;
\r
262 if ( sigismember(¤t->blocked, SIGINT) ) {
\r
263 dbg_printk("swtd_release get SIGINT signal\n");
\r
264 goto swtd_release_l1;
\r
266 #ifdef VICTOR_DEBUG // add by Victor Yu. 02-23-2006
\r
267 if ( sigismember(¤t->parent->blocked, SIGSTOP) ) {
\r
268 dbg_printk("swtd_release get SIGSTOP signal\n");
\r
270 if ( sigismember(¤t->parent->blocked, SIGHUP) ) {
\r
271 dbg_printk("swtd_release get SIGHUP signal\n");
\r
273 if ( sigismember(¤t->parent->blocked, SIGQUIT) ) {
\r
274 dbg_printk("swtd_release get SIGQUIT signal\n");
\r
276 if ( sigismember(¤t->parent->blocked, SIGTSTP) ) {
\r
277 dbg_printk("swtd_release get SIGTSTP signal\n");
\r
279 if ( sigismember(¤t->parent->blocked, SIGABRT) ) {
\r
280 dbg_printk("swtd_release get SIGABRT signal\n");
\r
282 if ( sigismember(¤t->parent->blocked, SIGSEGV) ) {
\r
283 dbg_printk("swtd_release get SIGSEGV signal\n");
\r
286 } else { // normal close the file handle
\r
289 if ( swtduserenabled ) {
\r
290 //swtd_disable(); //Johnson Liu remark 2009-03-27
\r
291 del_timer(&swtdtimer);
\r
292 swtduserenabled = 0;
\r
293 #if 0 // mask by Victor Yu. 05-15-2006
\r
294 swtdtime = DEFAULT_WATCHDOG_TIME;
\r
295 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
\r
296 add_timer(&swtdtimer);
\r
300 #if 0 // mask by Victor Yu. 02-23-2006
\r
305 #if 0 // mask by Victor Yu. 05-15-1006
\r
306 spin_unlock(&swtdlock);
\r
307 #else // add by Victor Yu. 05-15-2006
\r
308 restore_flags(flags);
\r
313 static void swtd_reboot(void *unused)
\r
315 char *argv[2], *envp[5];
\r
317 if ( in_interrupt() )
\r
319 if ( !current->fs->root )
\r
321 argv[0] = "/sbin/reboot";
\r
323 envp[0] = "HOME=/";
\r
324 envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
\r
326 call_usermodehelper(argv[0], argv, envp, 0);
\r
329 static void swtd_poll(unsigned long ignore)
\r
331 unsigned long flags;
\r
333 #if 0 // mask by Victor Yu. 05-15-2006
\r
334 spin_lock(&swtdlock);
\r
335 #else // add by Victor Yu. 05-15-2006
\r
340 #if 0 // add by Victor Yu. 08-31-2006
\r
342 #else // add by Jared Wu. 03-10-2009 Ack the watchdog instead of disable it.
\r
343 // this means that if the kernel crashed, the hardware watchdog will
\r
345 swtd_enable_with_time(50000);
\r
347 del_timer(&swtdtimer);
\r
348 #if 0 // mask by Victor Yu. 05-15-2006
\r
349 if ( swtduserenabled ) {
\r
350 dbg_printk("Now reboot the system.\n");
\r
351 schedule_work(&rebootqueue);
\r
353 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
\r
354 add_timer(&swtdtimer);
\r
357 spin_unlock(&swtdlock);
\r
358 #else // add by Victor Yu. 05-15-2006
\r
359 dbg_printk("Now reboot the system.\n");
\r
360 schedule_work(&rebootqueue);
\r
361 restore_flags(flags);
\r
365 static int swtd_proc_output(char *buf)
\r
371 "user enable\t: %d\n"
\r
372 "ack time\t: %d msec\n",
\r
373 swtduserenabled, (int)swtdtime);
\r
377 static int swtd_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
\r
379 int len=swtd_proc_output(page);
\r
381 if ( len <= off + count )
\r
383 *start = page + off;
\r
392 static struct file_operations swtd_fops = {
\r
397 release:swtd_release,
\r
399 static struct miscdevice swtd_dev = {
\r
400 MOXA_WATCHDOG_MINOR,
\r
405 static void __exit moxa_swtd_exit(void)
\r
407 unsigned long flags;
\r
409 #if 0 // mask by Victor Yu. 05-15-2006
\r
410 spin_lock(&swtdlock);
\r
411 #else // add by Victor Yu. 05-15-2006
\r
415 if ( swtduserenabled ) {
\r
417 del_timer(&swtdtimer);
\r
418 swtduserenabled = 0;
\r
421 #if 0 // mask by Victor Yu. 05-15-2006
\r
422 spin_unlock(&swtdlock);
\r
423 #else // add by Victor Yu. 05-15-2006
\r
424 restore_flags(flags);
\r
426 misc_deregister(&swtd_dev);
\r
429 static int __init moxa_swtd_init(void)
\r
432 if ( misc_register(&swtd_dev) ) {
\r
433 printk("Moxa ART CPU WatchDog: Register misc fail !\n");
\r
434 goto moxa_swtd_init_err;
\r
436 INIT_WORK(&rebootqueue, swtd_reboot, NULL);
\r
437 #if 0 // mask by Victor Yu. 05-15-2006
\r
438 spin_lock(&swtdlock);
\r
440 init_timer(&swtdtimer);
\r
441 swtdtimer.function = swtd_poll;
\r
442 #if 0 // mask by Victor Yu. 05-15-2006
\r
443 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
\r
444 add_timer(&swtdtimer);
\r
446 spin_unlock(&swtdlock);
\r
448 create_proc_read_entry("driver/swtd", 0, 0, swtd_read_proc, NULL);
\r
449 printk("Moxa ART CPU WatchDog driver v" MOXA_WATCHDOG_VERSION "\n");
\r
453 moxa_swtd_init_err:
\r
454 misc_deregister(&swtd_dev);
\r
458 module_init(moxa_swtd_init);
\r
459 module_exit(moxa_swtd_exit);
\r
461 MODULE_AUTHOR("Victor Yu");
\r
462 MODULE_LICENSE("GPL");
\r