MOXA linux-2.6.x / linux-2.6.9-uc0 from sdlinux-moxaart.tgz
[linux-2.6.9-moxart.git] / drivers / char / moxa_watchdog.c-09032718
blobe766a93793d18f10f34ca2a3ed6bb9a3100ea748
1 /*\r
2  * History:\r
3  * Date         Aurhor                  Comment\r
4  * 02-23-2006   Victor Yu.              Create it.\r
5  */\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
28 #include <asm/io.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
35 #ifdef VICTOR_DEBUG\r
36 #define dbg_printk(x...)        printk(x)\r
37 #else\r
38 #define dbg_printk(x...)\r
39 #endif\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
53 #endif\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
62         // reset in 5 sec.\r
63 static void swtd_enable_with_time(unsigned long swtd_time)\r
64 {\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
69 }\r
70 #endif\r
72 static void swtd_enable(void)\r
73 {\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
77 }\r
79 static void swtd_disable(void)\r
80 {\r
81         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0;\r
82 }\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
89         int             mode;\r
90         unsigned long   time;\r
91 };\r
92 static int swtd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)\r
93 {\r
94         unsigned long           time;\r
95         struct swtd_set_struct  nowset;\r
96         unsigned long           flags;\r
98         switch ( cmd ) {\r
99         case IOCTL_WATCHDOG_ENABLE :\r
100                 if ( copy_from_user(&time, (void *)arg, sizeof(time)) )\r
101                         return -EFAULT;\r
102                 if ( time < WATCHDOG_MIN_TIME || time > WATCHDOG_MAX_TIME )\r
103                         return -EINVAL;\r
104 #if 0   // mask by Victor Yu. 05-15-2006\r
105                 spin_lock(&swtdlock);\r
106                 del_timer(&swtdtimer);\r
107                 swtd_disable();\r
108                 swtdtime = time;\r
109                 swtduserenabled = 1;\r
110                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
111                 add_timer(&swtdtimer);\r
112                 swtd_enable();\r
113                 spin_unlock(&swtdlock);\r
114 #else   // add by Victor Yu. 05-15-2006\r
115                 save_flags(flags);\r
116                 cli();\r
117                 if ( swtduserenabled ) {\r
118                         swtd_disable();\r
119                         del_timer(&swtdtimer);\r
120                 }\r
121                 swtdtime = time;\r
122                 swtduserenabled = 1;\r
123                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
124                 add_timer(&swtdtimer);\r
125                 swtd_enable();\r
126                 restore_flags(flags);\r
127 #endif\r
128                 break;\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
133                         swtd_disable();\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
139                         swtd_enable();\r
140                 }\r
141                 spin_unlock(&swtdlock);\r
142 #else   // add by Victor Yu. 05-15-2006\r
143                 save_flags(flags);\r
144                 cli();\r
145                 if ( swtduserenabled ) {\r
146                         swtd_disable();\r
147                         del_timer(&swtdtimer);\r
148                         swtduserenabled = 0;\r
149                 }\r
150                 restore_flags(flags);\r
151 #endif\r
152                 break;\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
157                         return -EFAULT;\r
158                 break;\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
163                         swtd_disable();\r
164                         del_timer(&swtdtimer);\r
165                         swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
166                         add_timer(&swtdtimer);\r
167                         swtd_enable();\r
168                 }\r
169                 spin_unlock(&swtdlock);\r
170 #else   // add by Victor Yu. 05-15-2006\r
171                 save_flags(flags);\r
172                 cli();\r
173                 if ( swtduserenabled ) {\r
174                         swtd_disable();\r
175                         del_timer(&swtdtimer);\r
176                         swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
177                         add_timer(&swtdtimer);\r
178                         swtd_enable();\r
179                 }\r
180                 restore_flags(flags);\r
181 #endif\r
182                 break;\r
183         default:\r
184                 return -EINVAL;\r
185         }\r
186         return 0;\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
194                 return -ENODEV;\r
195 #if 0   // mask by Victor Yu. 05-15-2006\r
196         spin_lock(&swtdlock);\r
197         opencounts++;\r
198         spin_unlock(&swtdlock);\r
199 #else   // add by Victor Yu. 05-15-2006\r
200         save_flags(flags);\r
201         cli();\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
207                 swtd_enable();\r
208         }\r
209 #endif\r
210         opencounts++;\r
211         restore_flags(flags);\r
212 #endif\r
213         return 0;\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
223         save_flags(flags);\r
224         cli();\r
225 #endif\r
226         dbg_printk("swtd_release entry.\n");\r
227         opencounts--;\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(&current->parent->blocked, SIGKILL) ) {\r
235                                 dbg_printk("swtd_release get SIGKILL signal\n");\r
236                                 goto swtd_release_l1;\r
237                         }\r
238                         if ( sigismember(&current->blocked, SIGKILL) ) {\r
239                                 dbg_printk("swtd_release get SIGKILL signal\n");\r
240                                 goto swtd_release_l1;\r
241                         }\r
242                         if ( sigismember(&current->parent->blocked, SIGCHLD) ) {\r
243                                 dbg_printk("swtd_release get SIGCHLD signal\n");\r
244                                 goto swtd_release_l1;\r
245                         }\r
246                         if ( sigismember(&current->blocked, SIGCHLD) ) {\r
247                                 dbg_printk("swtd_release get SIGCHLD signal\n");\r
248                                 goto swtd_release_l1;\r
249                         }\r
250                         if ( sigismember(&current->parent->blocked, SIGTERM) ) {\r
251                                 dbg_printk("swtd_release get SIGTERM signal\n");\r
252                                 goto swtd_release_l1;\r
253                         }\r
254                         if ( sigismember(&current->blocked, SIGTERM) ) {\r
255                                 dbg_printk("swtd_release get SIGTERM signal\n");\r
256                                 goto swtd_release_l1;\r
257                         }\r
258                         if ( sigismember(&current->parent->blocked, SIGINT) ) {\r
259                                 dbg_printk("swtd_release get SIGINT signal\n");\r
260                                 goto swtd_release_l1;\r
261                         }\r
262                         if ( sigismember(&current->blocked, SIGINT) ) {\r
263                                 dbg_printk("swtd_release get SIGINT signal\n");\r
264                                 goto swtd_release_l1;\r
265                         }\r
266 #ifdef VICTOR_DEBUG     // add by Victor Yu. 02-23-2006\r
267                         if ( sigismember(&current->parent->blocked, SIGSTOP) ) {\r
268                                 dbg_printk("swtd_release get SIGSTOP signal\n");\r
269                         }\r
270                         if ( sigismember(&current->parent->blocked, SIGHUP) ) {\r
271                                 dbg_printk("swtd_release get SIGHUP signal\n");\r
272                         }\r
273                         if ( sigismember(&current->parent->blocked, SIGQUIT) ) {\r
274                                 dbg_printk("swtd_release get SIGQUIT signal\n");\r
275                         }\r
276                         if ( sigismember(&current->parent->blocked, SIGTSTP) ) {\r
277                                 dbg_printk("swtd_release get SIGTSTP signal\n");\r
278                         }\r
279                         if ( sigismember(&current->parent->blocked, SIGABRT) ) {\r
280                                 dbg_printk("swtd_release get SIGABRT signal\n");\r
281                         }\r
282                         if ( sigismember(&current->parent->blocked, SIGSEGV) ) {\r
283                                 dbg_printk("swtd_release get SIGSEGV signal\n");\r
284                         }\r
285 #endif\r
286                 } else {        // normal close the file handle\r
287 swtd_release_l1:\r
288 #endif\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
297                                 swtd_enable();\r
298 #endif\r
299                         }\r
300 #if 0   // mask by Victor Yu. 02-23-2006\r
301                 }\r
302 #endif\r
303                 opencounts = 0;\r
304         }\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
309 #endif\r
310         return 0;\r
313 static void swtd_reboot(void *unused)\r
315         char    *argv[2], *envp[5];\r
317         if ( in_interrupt() )\r
318                 return;\r
319         if ( !current->fs->root )\r
320                 return;\r
321         argv[0] = "/sbin/reboot";\r
322         argv[1] = 0;\r
323         envp[0] = "HOME=/";\r
324         envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";\r
325         envp[2] = 0;\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
336         save_flags(flags);\r
337         cli();\r
338 #endif\r
340 #if 0   // add by Victor Yu. 08-31-2006\r
341         swtd_disable();\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
344         // reset in 5 sec.\r
345         swtd_enable_with_time(50000);\r
346 #endif\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
352         } else {\r
353                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
354                 add_timer(&swtdtimer);\r
355                 swtd_enable();\r
356         }\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
362 #endif\r
365 static int swtd_proc_output(char *buf)\r
367         char    *p;\r
369         p = buf;\r
370         p += sprintf(p,\r
371                      "user enable\t: %d\n"\r
372                      "ack time\t: %d msec\n",\r
373                      swtduserenabled, (int)swtdtime);\r
374         return p - buf;\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
382                 *eof = 1;\r
383         *start = page + off;\r
384         len -= off;\r
385         if ( len > count )\r
386                 len = count;\r
387         if ( len < 0 )\r
388                 len = 0;\r
389         return len;\r
392 static struct file_operations swtd_fops = {\r
393         owner:THIS_MODULE,\r
394         llseek:NULL,\r
395         ioctl:swtd_ioctl,\r
396         open:swtd_open,\r
397         release:swtd_release,\r
398 };\r
399 static struct miscdevice swtd_dev = {\r
400         MOXA_WATCHDOG_MINOR,\r
401         "swtd",\r
402         &swtd_fops\r
403 };\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
412         save_flags(flags);\r
413         cli();\r
414 #endif\r
415         if ( swtduserenabled ) {\r
416                 swtd_disable();\r
417                 del_timer(&swtdtimer);\r
418                 swtduserenabled = 0;\r
419                 opencounts = 0;\r
420         }\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
425 #endif\r
426         misc_deregister(&swtd_dev);\r
429 static int __init moxa_swtd_init(void)\r
431         // register misc\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
435         }\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
439 #endif\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
445         swtd_enable();\r
446         spin_unlock(&swtdlock);\r
447 #endif\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
451         return 0;\r
453 moxa_swtd_init_err:\r
454         misc_deregister(&swtd_dev);\r
455         return -ENOMEM;\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