MOXA linux-2.6.x / linux-2.6.19-uc1 from UC-7110-LX-BOOTLOADER-1.9_VERSION-4.2.tgz
[linux-2.6.19-moxart.git] / drivers / char / watchdog / uc7110wdt.c
blobd0f5d77c36023b32e374fd47c1671e931ee4c25c
1 /*
2 * History:
3 * Date Aurhor Comment
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.
7 */
8 //#define __KERNEL_SYSCALLS__
9 #if 1 // add by Victor Yu. 02-09-2007
10 #include <linux/version.h>
11 #endif
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>
31 #include <linux/mm.h>
32 #include <linux/kmod.h>
33 #include <asm/io.h>
34 #include <asm/uaccess.h>
35 #include <asm/system.h>
36 #include <asm/irq.h>
37 #include <asm/bitops.h>
38 #include <asm/arch/moxa.h>
40 //#define VICTOR_DEBUG
41 #ifdef VICTOR_DEBUG
42 #define dbg_printk(x...) printk(x)
43 #else
44 #define dbg_printk(x...)
45 #endif
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;
59 #endif
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 {
83 int mode;
84 unsigned long time;
86 static int swtd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
88 unsigned long time;
89 struct swtd_set_struct nowset;
90 unsigned long flags;
92 switch ( cmd ) {
93 case IOCTL_WATCHDOG_ENABLE :
94 if ( copy_from_user(&time, (void *)arg, sizeof(time)) )
95 return -EFAULT;
96 if ( time < WATCHDOG_MIN_TIME || time > WATCHDOG_MAX_TIME )
97 return -EINVAL;
98 #if 0 // mask by Victor Yu. 05-15-2006
99 spin_lock(&swtdlock);
100 del_timer(&swtdtimer);
101 swtd_disable();
102 swtdtime = time;
103 swtduserenabled = 1;
104 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
105 add_timer(&swtdtimer);
106 swtd_enable();
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);
111 #else
112 save_flags(flags);
113 cli();
114 #endif // LINUX_VERSION_CODE
115 if ( swtduserenabled ) {
116 swtd_disable();
117 del_timer(&swtdtimer);
119 swtdtime = time;
120 swtduserenabled = 1;
121 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
122 add_timer(&swtdtimer);
123 swtd_enable();
124 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
125 local_irq_restore(flags);
126 #else
127 restore_flags(flags);
128 #endif // LINUX_VERSION_CODE
129 #endif
130 break;
131 case IOCTL_WATCHDOG_DISABLE :
132 #if 0 // mask by Victor Yu. 05-15-2006
133 spin_lock(&swtdlock);
134 if ( swtduserenabled ) {
135 swtd_disable();
136 del_timer(&swtdtimer);
137 swtduserenabled = 0;
138 swtdtime = DEFAULT_WATCHDOG_TIME;
139 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
140 add_timer(&swtdtimer);
141 swtd_enable();
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);
147 #else
148 save_flags(flags);
149 cli();
150 #endif // LINUX_VERSION_CODE
151 if ( swtduserenabled ) {
152 swtd_disable();
153 del_timer(&swtdtimer);
154 swtduserenabled = 0;
156 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
157 local_irq_restore(flags);
158 #else
159 restore_flags(flags);
160 #endif // LINUX_VERSION_CODE
161 #endif
162 break;
163 case IOCTL_WATCHDOG_GET_SETTING :
164 nowset.mode = swtduserenabled;
165 nowset.time = swtdtime;
166 if ( copy_to_user((void *)arg, &nowset, sizeof(nowset)) )
167 return -EFAULT;
168 break;
169 case IOCTL_WATCHDOG_ACK :
170 #if 0 // mask by Victor Yu. 05-15-2006
171 spin_lock(&swtdlock);
172 if ( swtduserenabled ) {
173 swtd_disable();
174 del_timer(&swtdtimer);
175 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
176 add_timer(&swtdtimer);
177 swtd_enable();
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);
183 #else
184 save_flags(flags);
185 cli();
186 #endif // LINUX_VERSION_CODE
187 if ( swtduserenabled ) {
188 swtd_disable();
189 del_timer(&swtdtimer);
190 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
191 add_timer(&swtdtimer);
192 swtd_enable();
194 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
195 local_irq_restore(flags);
196 #else
197 restore_flags(flags);
198 #endif // LINUX_VERSION_CODE
199 #endif
200 break;
201 default:
202 return -EINVAL;
204 return 0;
207 static int swtd_open(struct inode *inode, struct file *file)
209 unsigned long flags;
211 if ( MINOR(inode->i_rdev) != MOXA_WATCHDOG_MINOR )
212 return -ENODEV;
213 #if 0 // mask by Victor Yu. 05-15-2006
214 spin_lock(&swtdlock);
215 opencounts++;
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);
220 #else
221 save_flags(flags);
222 cli();
223 #endif // LINUX_VERSION_CODE
224 #if 0 // mask by Victor Yu. 05-15-2006
225 if ( swtduserenabled == 0 ) {
226 swtduserenabled = 1;
227 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
228 add_timer(&swtdtimer);
229 swtd_enable();
231 #endif
232 opencounts++;
233 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
234 local_irq_restore(flags);
235 #else
236 restore_flags(flags);
237 #endif // LINUX_VERSION_CODE
238 #endif
239 return 0;
242 static int swtd_release(struct inode *inode, struct file *file)
244 unsigned long flags;
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);
251 #else
252 save_flags(flags);
253 cli();
254 #endif // LINUX_VERSION_CODE
255 #endif
256 dbg_printk("swtd_release entry.\n");
257 opencounts--;
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(&current->parent->blocked, SIGKILL) ) {
265 dbg_printk("swtd_release get SIGKILL signal\n");
266 goto swtd_release_l1;
268 if ( sigismember(&current->blocked, SIGKILL) ) {
269 dbg_printk("swtd_release get SIGKILL signal\n");
270 goto swtd_release_l1;
272 if ( sigismember(&current->parent->blocked, SIGCHLD) ) {
273 dbg_printk("swtd_release get SIGCHLD signal\n");
274 goto swtd_release_l1;
276 if ( sigismember(&current->blocked, SIGCHLD) ) {
277 dbg_printk("swtd_release get SIGCHLD signal\n");
278 goto swtd_release_l1;
280 if ( sigismember(&current->parent->blocked, SIGTERM) ) {
281 dbg_printk("swtd_release get SIGTERM signal\n");
282 goto swtd_release_l1;
284 if ( sigismember(&current->blocked, SIGTERM) ) {
285 dbg_printk("swtd_release get SIGTERM signal\n");
286 goto swtd_release_l1;
288 if ( sigismember(&current->parent->blocked, SIGINT) ) {
289 dbg_printk("swtd_release get SIGINT signal\n");
290 goto swtd_release_l1;
292 if ( sigismember(&current->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(&current->parent->blocked, SIGSTOP) ) {
298 dbg_printk("swtd_release get SIGSTOP signal\n");
300 if ( sigismember(&current->parent->blocked, SIGHUP) ) {
301 dbg_printk("swtd_release get SIGHUP signal\n");
303 if ( sigismember(&current->parent->blocked, SIGQUIT) ) {
304 dbg_printk("swtd_release get SIGQUIT signal\n");
306 if ( sigismember(&current->parent->blocked, SIGTSTP) ) {
307 dbg_printk("swtd_release get SIGTSTP signal\n");
309 if ( sigismember(&current->parent->blocked, SIGABRT) ) {
310 dbg_printk("swtd_release get SIGABRT signal\n");
312 if ( sigismember(&current->parent->blocked, SIGSEGV) ) {
313 dbg_printk("swtd_release get SIGSEGV signal\n");
315 #endif
316 } else { // normal close the file handle
317 swtd_release_l1:
318 #endif
319 if ( swtduserenabled ) {
320 swtd_disable();
321 del_timer(&swtdtimer);
322 swtduserenabled = 0;
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);
327 swtd_enable();
328 #endif
330 #if 0 // mask by Victor Yu. 02-23-2006
332 #endif
333 opencounts = 0;
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);
340 #else
341 restore_flags(flags);
342 #endif // LINUX_VERSION_CODE
343 #endif
344 return 0;
347 static void swtd_reboot(void *unused)
349 char *argv[2], *envp[5];
351 if ( in_interrupt() )
352 return;
353 if ( !current->fs->root )
354 return;
355 argv[0] = "/bin/reboot";
356 argv[1] = 0;
357 envp[0] = "HOME=/";
358 envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
359 envp[2] = 0;
360 call_usermodehelper(argv[0], argv, envp, 0);
363 static void swtd_poll(unsigned long ignore)
365 unsigned long flags;
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);
372 #else
373 save_flags(flags);
374 cli();
375 #endif // LINUX_VERSION_CODE
376 #endif
377 swtd_disable();
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);
383 } else {
384 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);
385 add_timer(&swtdtimer);
386 swtd_enable();
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);
394 #else
395 restore_flags(flags);
396 #endif // LINUX_VERSION_CODE
397 #endif
400 static int swtd_proc_output(char *buf)
402 char *p;
404 p = buf;
405 p += sprintf(p,
406 "user enable\t: %d\n"
407 "ack time\t: %d msec\n",
408 swtduserenabled, (int)swtdtime);
409 return p - buf;
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 )
417 *eof = 1;
418 *start = page + off;
419 len -= off;
420 if ( len > count )
421 len = count;
422 if ( len < 0 )
423 len = 0;
424 return len;
427 static struct file_operations swtd_fops = {
428 owner:THIS_MODULE,
429 llseek:NULL,
430 ioctl:swtd_ioctl,
431 open:swtd_open,
432 release:swtd_release,
434 static struct miscdevice swtd_dev = {
435 MOXA_WATCHDOG_MINOR,
436 "swtd",
437 &swtd_fops
440 static void __exit moxa_swtd_exit(void)
442 unsigned long flags;
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);
449 #else
450 save_flags(flags);
451 cli();
452 #endif // LINUX_VERSION_CODE
453 #endif
454 if ( swtduserenabled ) {
455 swtd_disable();
456 del_timer(&swtdtimer);
457 swtduserenabled = 0;
458 opencounts = 0;
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);
465 #else
466 restore_flags(flags);
467 #endif // LINUX_VERSION_CODE
468 #endif
469 misc_deregister(&swtd_dev);
472 static int __init moxa_swtd_init(void)
474 // register misc
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);
482 #endif
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);
488 swtd_enable();
489 spin_unlock(&swtdlock);
490 #endif
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");
494 return 0;
496 moxa_swtd_init_err:
497 misc_deregister(&swtd_dev);
498 return -ENOMEM;
501 module_init(moxa_swtd_init);
502 module_exit(moxa_swtd_exit);
504 MODULE_AUTHOR("Victor Yu");
505 MODULE_LICENSE("GPL");