4 * 02-23-2006 Victor Yu. Create it.
5 * 08-15-2006 Victor Yu. Modify to support UC-7110.
6 * 02-09-2007 Victor Yu. Porting to kernel version 2.6.19.
8 //#define __KERNEL_SYSCALLS__
9 #if 1 // add by Victor Yu. 02-09-2007
10 #include <linux/version.h>
12 //#include <linux/config.h>
13 #include <linux/proc_fs.h>
14 //#include <linux/devfs_fs_kernel.h>
15 #include <linux/unistd.h>
16 #include <linux/string.h>
17 #include <linux/ctype.h>
18 #include <linux/module.h>
19 #include <linux/kernel.h>
20 #include <linux/miscdevice.h>
21 #include <linux/fcntl.h>
22 #include <linux/init.h>
23 #include <linux/poll.h>
24 #include <linux/spinlock.h>
25 #include <linux/delay.h>
26 #include <linux/timer.h>
27 #include <linux/ioport.h>
28 #include <linux/interrupt.h>
29 #include <linux/sched.h>
30 #include <linux/signal.h>
32 #include <linux/kmod.h>
34 #include <asm/uaccess.h>
35 #include <asm/system.h>
37 #include <asm/bitops.h>
38 #include <asm/arch/moxa.h>
40 //#define VICTOR_DEBUG
42 #define dbg_printk(x...) printk(x)
44 #define dbg_printk(x...)
47 #define MOXA_WATCHDOG_VERSION "1.0"
48 #define MOXA_WATCHDOG_MINOR WATCHDOG_MINOR
49 #define DEFAULT_WATCHDOG_TIME (30UL*1000UL) // 30 seconds
50 #define WATCHDOG_MIN_TIME 50UL // 50 msec
51 #define WATCHDOG_MAX_TIME (60UL*1000UL) // 60 seconds
52 #define WATCHDOG_TOL_TIME (500UL) // 500 msec, for watchdog timer polling
53 #define WATCHDOG_TOL_COUNT_TIME (1000UL) // 1 seconds, for watchdog timer count
54 #define WATCHDOG_COUNTER(x) ((APB_CLK/1000UL)*(x))
55 #define WATCHDOG_JIFFIES(x) ((((x)+WATCHDOG_TOL_TIME)*HZ)/1000UL)
57 #if 0 // mask by Victor Yu. 05-15-2006
58 static spinlock_t swtdlock
=SPIN_LOCK_UNLOCKED
;
60 static int opencounts
=0;
61 static int swtduserenabled
=0;
62 static unsigned long swtdtime
=DEFAULT_WATCHDOG_TIME
;
63 static struct timer_list swtdtimer
;
64 static struct work_struct rebootqueue
;
66 static void swtd_enable(void)
68 *(volatile unsigned int *)(CPE_WATCHDOG_BASE
+4) = WATCHDOG_COUNTER(swtdtime
+WATCHDOG_TOL_COUNT_TIME
);
69 *(volatile unsigned int *)(CPE_WATCHDOG_BASE
+8) = 0x5ab9;
70 *(volatile unsigned int *)(CPE_WATCHDOG_BASE
+12) = 0x03;
73 static void swtd_disable(void)
75 *(volatile unsigned int *)(CPE_WATCHDOG_BASE
+12) = 0;
78 #define IOCTL_WATCHDOG_ENABLE 1 // enable watch dog and set time (unint msec)
79 #define IOCTL_WATCHDOG_DISABLE 2 // disable watch dog, kernle do it
80 #define IOCTL_WATCHDOG_GET_SETTING 3 // get now setting about mode and time
81 #define IOCTL_WATCHDOG_ACK 4 // to ack watch dog
82 struct swtd_set_struct
{
86 static int swtd_ioctl(struct inode
*inode
, struct file
*file
, unsigned int cmd
, unsigned long arg
)
89 struct swtd_set_struct nowset
;
93 case IOCTL_WATCHDOG_ENABLE
:
94 if ( copy_from_user(&time
, (void *)arg
, sizeof(time
)) )
96 if ( time
< WATCHDOG_MIN_TIME
|| time
> WATCHDOG_MAX_TIME
)
98 #if 0 // mask by Victor Yu. 05-15-2006
100 del_timer(&swtdtimer
);
104 swtdtimer
.expires
= jiffies
+ WATCHDOG_JIFFIES(swtdtime
);
105 add_timer(&swtdtimer
);
107 spin_unlock(&swtdlock
);
108 #else // add by Victor Yu. 05-15-2006
109 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
110 local_irq_save(flags
);
114 #endif // LINUX_VERSION_CODE
115 if ( swtduserenabled
) {
117 del_timer(&swtdtimer
);
121 swtdtimer
.expires
= jiffies
+ WATCHDOG_JIFFIES(swtdtime
);
122 add_timer(&swtdtimer
);
124 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
125 local_irq_restore(flags
);
127 restore_flags(flags
);
128 #endif // LINUX_VERSION_CODE
131 case IOCTL_WATCHDOG_DISABLE
:
132 #if 0 // mask by Victor Yu. 05-15-2006
133 spin_lock(&swtdlock
);
134 if ( swtduserenabled
) {
136 del_timer(&swtdtimer
);
138 swtdtime
= DEFAULT_WATCHDOG_TIME
;
139 swtdtimer
.expires
= jiffies
+ WATCHDOG_JIFFIES(swtdtime
);
140 add_timer(&swtdtimer
);
143 spin_unlock(&swtdlock
);
144 #else // add by Victor Yu. 05-15-2006
145 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
146 local_irq_save(flags
);
150 #endif // LINUX_VERSION_CODE
151 if ( swtduserenabled
) {
153 del_timer(&swtdtimer
);
156 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
157 local_irq_restore(flags
);
159 restore_flags(flags
);
160 #endif // LINUX_VERSION_CODE
163 case IOCTL_WATCHDOG_GET_SETTING
:
164 nowset
.mode
= swtduserenabled
;
165 nowset
.time
= swtdtime
;
166 if ( copy_to_user((void *)arg
, &nowset
, sizeof(nowset
)) )
169 case IOCTL_WATCHDOG_ACK
:
170 #if 0 // mask by Victor Yu. 05-15-2006
171 spin_lock(&swtdlock
);
172 if ( swtduserenabled
) {
174 del_timer(&swtdtimer
);
175 swtdtimer
.expires
= jiffies
+ WATCHDOG_JIFFIES(swtdtime
);
176 add_timer(&swtdtimer
);
179 spin_unlock(&swtdlock
);
180 #else // add by Victor Yu. 05-15-2006
181 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
182 local_irq_save(flags
);
186 #endif // LINUX_VERSION_CODE
187 if ( swtduserenabled
) {
189 del_timer(&swtdtimer
);
190 swtdtimer
.expires
= jiffies
+ WATCHDOG_JIFFIES(swtdtime
);
191 add_timer(&swtdtimer
);
194 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
195 local_irq_restore(flags
);
197 restore_flags(flags
);
198 #endif // LINUX_VERSION_CODE
207 static int swtd_open(struct inode
*inode
, struct file
*file
)
211 if ( MINOR(inode
->i_rdev
) != MOXA_WATCHDOG_MINOR
)
213 #if 0 // mask by Victor Yu. 05-15-2006
214 spin_lock(&swtdlock
);
216 spin_unlock(&swtdlock
);
217 #else // add by Victor Yu. 05-15-2006
218 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
219 local_irq_save(flags
);
223 #endif // LINUX_VERSION_CODE
224 #if 0 // mask by Victor Yu. 05-15-2006
225 if ( swtduserenabled
== 0 ) {
227 swtdtimer
.expires
= jiffies
+ WATCHDOG_JIFFIES(swtdtime
);
228 add_timer(&swtdtimer
);
233 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
234 local_irq_restore(flags
);
236 restore_flags(flags
);
237 #endif // LINUX_VERSION_CODE
242 static int swtd_release(struct inode
*inode
, struct file
*file
)
246 #if 0 // mask by Victor Yu. 05-15-2006
247 spin_lock(&swtdlock
);
248 #else // add by Victor Yu. 05-15-2006
249 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
250 local_irq_save(flags
);
254 #endif // LINUX_VERSION_CODE
256 dbg_printk("swtd_release entry.\n");
258 if ( opencounts
<= 0 ) {
259 #if 0 // mask by Victor Yu. 02-23-2006
260 if ( signal_pending(current
) ) {
261 dbg_printk("swtd_release has signal pending\n");
262 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]);
263 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]);
264 if ( sigismember(¤t
->parent
->blocked
, SIGKILL
) ) {
265 dbg_printk("swtd_release get SIGKILL signal\n");
266 goto swtd_release_l1
;
268 if ( sigismember(¤t
->blocked
, SIGKILL
) ) {
269 dbg_printk("swtd_release get SIGKILL signal\n");
270 goto swtd_release_l1
;
272 if ( sigismember(¤t
->parent
->blocked
, SIGCHLD
) ) {
273 dbg_printk("swtd_release get SIGCHLD signal\n");
274 goto swtd_release_l1
;
276 if ( sigismember(¤t
->blocked
, SIGCHLD
) ) {
277 dbg_printk("swtd_release get SIGCHLD signal\n");
278 goto swtd_release_l1
;
280 if ( sigismember(¤t
->parent
->blocked
, SIGTERM
) ) {
281 dbg_printk("swtd_release get SIGTERM signal\n");
282 goto swtd_release_l1
;
284 if ( sigismember(¤t
->blocked
, SIGTERM
) ) {
285 dbg_printk("swtd_release get SIGTERM signal\n");
286 goto swtd_release_l1
;
288 if ( sigismember(¤t
->parent
->blocked
, SIGINT
) ) {
289 dbg_printk("swtd_release get SIGINT signal\n");
290 goto swtd_release_l1
;
292 if ( sigismember(¤t
->blocked
, SIGINT
) ) {
293 dbg_printk("swtd_release get SIGINT signal\n");
294 goto swtd_release_l1
;
296 #ifdef VICTOR_DEBUG // add by Victor Yu. 02-23-2006
297 if ( sigismember(¤t
->parent
->blocked
, SIGSTOP
) ) {
298 dbg_printk("swtd_release get SIGSTOP signal\n");
300 if ( sigismember(¤t
->parent
->blocked
, SIGHUP
) ) {
301 dbg_printk("swtd_release get SIGHUP signal\n");
303 if ( sigismember(¤t
->parent
->blocked
, SIGQUIT
) ) {
304 dbg_printk("swtd_release get SIGQUIT signal\n");
306 if ( sigismember(¤t
->parent
->blocked
, SIGTSTP
) ) {
307 dbg_printk("swtd_release get SIGTSTP signal\n");
309 if ( sigismember(¤t
->parent
->blocked
, SIGABRT
) ) {
310 dbg_printk("swtd_release get SIGABRT signal\n");
312 if ( sigismember(¤t
->parent
->blocked
, SIGSEGV
) ) {
313 dbg_printk("swtd_release get SIGSEGV signal\n");
316 } else { // normal close the file handle
319 if ( swtduserenabled
) {
321 del_timer(&swtdtimer
);
323 #if 0 // mask by Victor Yu. 05-15-2006
324 swtdtime
= DEFAULT_WATCHDOG_TIME
;
325 swtdtimer
.expires
= jiffies
+ WATCHDOG_JIFFIES(swtdtime
);
326 add_timer(&swtdtimer
);
330 #if 0 // mask by Victor Yu. 02-23-2006
335 #if 0 // mask by Victor Yu. 05-15-1006
336 spin_unlock(&swtdlock
);
337 #else // add by Victor Yu. 05-15-2006
338 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
339 local_irq_restore(flags
);
341 restore_flags(flags
);
342 #endif // LINUX_VERSION_CODE
347 static void swtd_reboot(void *unused
)
349 char *argv
[2], *envp
[5];
351 if ( in_interrupt() )
353 if ( !current
->fs
->root
)
355 argv
[0] = "/bin/reboot";
358 envp
[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
360 call_usermodehelper(argv
[0], argv
, envp
, 0);
363 static void swtd_poll(unsigned long ignore
)
367 #if 0 // mask by Victor Yu. 05-15-2006
368 spin_lock(&swtdlock
);
369 #else // add by Victor Yu. 05-15-2006
370 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
371 local_irq_save(flags
);
375 #endif // LINUX_VERSION_CODE
378 del_timer(&swtdtimer
);
379 #if 0 // mask by Victor Yu. 05-15-2006
380 if ( swtduserenabled
) {
381 dbg_printk("Now reboot the system.\n");
382 schedule_work(&rebootqueue
);
384 swtdtimer
.expires
= jiffies
+ WATCHDOG_JIFFIES(swtdtime
);
385 add_timer(&swtdtimer
);
388 spin_unlock(&swtdlock
);
389 #else // add by Victor Yu. 05-15-2006
390 dbg_printk("Now reboot the system.\n");
391 schedule_work(&rebootqueue
);
392 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
393 local_irq_restore(flags
);
395 restore_flags(flags
);
396 #endif // LINUX_VERSION_CODE
400 static int swtd_proc_output(char *buf
)
406 "user enable\t: %d\n"
407 "ack time\t: %d msec\n",
408 swtduserenabled
, (int)swtdtime
);
412 static int swtd_read_proc(char *page
, char **start
, off_t off
, int count
, int *eof
, void *data
)
414 int len
=swtd_proc_output(page
);
416 if ( len
<= off
+ count
)
427 static struct file_operations swtd_fops
= {
432 release
:swtd_release
,
434 static struct miscdevice swtd_dev
= {
440 static void __exit
moxa_swtd_exit(void)
444 #if 0 // mask by Victor Yu. 05-15-2006
445 spin_lock(&swtdlock
);
446 #else // add by Victor Yu. 05-15-2006
447 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
448 local_irq_save(flags
);
452 #endif // LINUX_VERSION_CODE
454 if ( swtduserenabled
) {
456 del_timer(&swtdtimer
);
460 #if 0 // mask by Victor Yu. 05-15-2006
461 spin_unlock(&swtdlock
);
462 #else // add by Victor Yu. 05-15-2006
463 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
464 local_irq_restore(flags
);
466 restore_flags(flags
);
467 #endif // LINUX_VERSION_CODE
469 misc_deregister(&swtd_dev
);
472 static int __init
moxa_swtd_init(void)
475 if ( misc_register(&swtd_dev
) ) {
476 printk("Moxa ART CPU WatchDog: Register misc fail !\n");
477 goto moxa_swtd_init_err
;
479 INIT_WORK(&rebootqueue
, swtd_reboot
, NULL
);
480 #if 0 // mask by Victor Yu. 05-15-2006
481 spin_lock(&swtdlock
);
483 init_timer(&swtdtimer
);
484 swtdtimer
.function
= swtd_poll
;
485 #if 0 // mask by Victor Yu. 05-15-2006
486 swtdtimer
.expires
= jiffies
+ WATCHDOG_JIFFIES(swtdtime
);
487 add_timer(&swtdtimer
);
489 spin_unlock(&swtdlock
);
491 create_proc_read_entry("driver/swtd", 0, 0, swtd_read_proc
, NULL
);
492 printk("Moxa ART CPU WatchDog driver v" MOXA_WATCHDOG_VERSION
"\n");
497 misc_deregister(&swtd_dev
);
501 module_init(moxa_swtd_init
);
502 module_exit(moxa_swtd_exit
);
504 MODULE_AUTHOR("Victor Yu");
505 MODULE_LICENSE("GPL");