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.new-05152006
blobe87db0c8128c46d0a33bf37e6cc766186344cdee
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 static void swtd_enable(void)\r
61 {\r
62         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+4) = WATCHDOG_COUNTER(swtdtime+WATCHDOG_TOL_COUNT_TIME);\r
63         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+8) = 0x5ab9;\r
64         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0x03;\r
65 }\r
67 static void swtd_disable(void)\r
68 {\r
69         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0;\r
70 }\r
72 #define IOCTL_WATCHDOG_ENABLE           1       // enable watch dog and set time (unint msec)\r
73 #define IOCTL_WATCHDOG_DISABLE          2       // disable watch dog, kernle do it\r
74 #define IOCTL_WATCHDOG_GET_SETTING      3       // get now setting about mode and time\r
75 #define IOCTL_WATCHDOG_ACK              4       // to ack watch dog\r
76 struct swtd_set_struct {\r
77         int             mode;\r
78         unsigned long   time;\r
79 };\r
80 static int swtd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)\r
81 {\r
82         unsigned long           time;\r
83         struct swtd_set_struct  nowset;\r
84         unsigned long           flags;\r
86         switch ( cmd ) {\r
87         case IOCTL_WATCHDOG_ENABLE :\r
88                 if ( copy_from_user(&time, (void *)arg, sizeof(time)) )\r
89                         return -EFAULT;\r
90                 if ( time < WATCHDOG_MIN_TIME || time > WATCHDOG_MAX_TIME )\r
91                         return -EINVAL;\r
92 #if 0   // mask by Victor Yu. 05-15-2006\r
93                 spin_lock(&swtdlock);\r
94                 del_timer(&swtdtimer);\r
95                 swtd_disable();\r
96                 swtdtime = time;\r
97                 swtduserenabled = 1;\r
98                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
99                 add_timer(&swtdtimer);\r
100                 swtd_enable();\r
101                 spin_unlock(&swtdlock);\r
102 #else   // add by Victor Yu. 05-15-2006\r
103                 save_flags(flags);\r
104                 cli();\r
105                 if ( swtduserenabled ) {\r
106                         swtd_disable();\r
107                         del_timer(&swtdtimer);\r
108                 }\r
109                 swtdtime = time;\r
110                 swtduserenabled = 1;\r
111                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
112                 add_timer(&swtdtimer);\r
113                 swtd_enable();\r
114                 restore_flags(flags);\r
115 #endif\r
116                 break;\r
117         case IOCTL_WATCHDOG_DISABLE :\r
118 #if 0   // mask by Victor Yu. 05-15-2006\r
119                 spin_lock(&swtdlock);\r
120                 if ( swtduserenabled ) {\r
121                         swtd_disable();\r
122                         del_timer(&swtdtimer);\r
123                         swtduserenabled = 0;\r
124                         swtdtime = DEFAULT_WATCHDOG_TIME;\r
125                         swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
126                         add_timer(&swtdtimer);\r
127                         swtd_enable();\r
128                 }\r
129                 spin_unlock(&swtdlock);\r
130 #else   // add by Victor Yu. 05-15-2006\r
131                 save_flags(flags);\r
132                 cli();\r
133                 if ( swtduserenabled ) {\r
134                         swtd_disable();\r
135                         del_timer(&swtdtimer);\r
136                         swtduserenabled = 0;\r
137                 }\r
138                 restore_flags(flags);\r
139 #endif\r
140                 break;\r
141         case IOCTL_WATCHDOG_GET_SETTING :\r
142                 nowset.mode = swtduserenabled;\r
143                 nowset.time = swtdtime;\r
144                 if ( copy_to_user((void *)arg, &nowset, sizeof(nowset)) )\r
145                         return -EFAULT;\r
146                 break;\r
147         case IOCTL_WATCHDOG_ACK :\r
148 #if 0   // mask by Victor Yu. 05-15-2006\r
149                 spin_lock(&swtdlock);\r
150                 if ( swtduserenabled ) {\r
151                         swtd_disable();\r
152                         del_timer(&swtdtimer);\r
153                         swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
154                         add_timer(&swtdtimer);\r
155                         swtd_enable();\r
156                 }\r
157                 spin_unlock(&swtdlock);\r
158 #else   // add by Victor Yu. 05-15-2006\r
159                 save_flags(flags);\r
160                 cli();\r
161                 if ( swtduserenabled ) {\r
162                         swtd_disable();\r
163                         del_timer(&swtdtimer);\r
164                         swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
165                         add_timer(&swtdtimer);\r
166                         swtd_enable();\r
167                 }\r
168                 restore_flags(flags);\r
169 #endif\r
170                 break;\r
171         default:\r
172                 return -EINVAL;\r
173         }\r
174         return 0;\r
177 static int swtd_open(struct inode *inode, struct file *file)\r
179         unsigned long   flags;\r
181         if ( MINOR(inode->i_rdev) != MOXA_WATCHDOG_MINOR )\r
182                 return -ENODEV;\r
183 #if 0   // mask by Victor Yu. 05-15-2006\r
184         spin_lock(&swtdlock);\r
185         opencounts++;\r
186         spin_unlock(&swtdlock);\r
187 #else   // add by Victor Yu. 05-15-2006\r
188         save_flags(flags);\r
189         cli();\r
190 #if 0   // mask by Victor Yu. 05-15-2006\r
191         if ( swtduserenabled == 0 ) {\r
192                 swtduserenabled = 1;\r
193                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
194                 add_timer(&swtdtimer);\r
195                 swtd_enable();\r
196         }\r
197 #endif\r
198         opencounts++;\r
199         restore_flags(flags);\r
200 #endif\r
201         return 0;\r
204 static int swtd_release(struct inode *inode, struct file *file)\r
206         unsigned long   flags;\r
208 #if 0   // mask by Victor Yu. 05-15-2006\r
209         spin_lock(&swtdlock);\r
210 #else   // add by Victor Yu. 05-15-2006\r
211         save_flags(flags);\r
212         cli();\r
213 #endif\r
214         dbg_printk("swtd_release entry.\n");\r
215         opencounts--;\r
216         if ( opencounts <= 0 ) {\r
217 #if 0   // mask by Victor Yu. 02-23-2006\r
218                 if ( signal_pending(current) ) {\r
219                         dbg_printk("swtd_release has signal pending\n");\r
220                         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
221                         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
222                         if ( sigismember(&current->parent->blocked, SIGKILL) ) {\r
223                                 dbg_printk("swtd_release get SIGKILL signal\n");\r
224                                 goto swtd_release_l1;\r
225                         }\r
226                         if ( sigismember(&current->blocked, SIGKILL) ) {\r
227                                 dbg_printk("swtd_release get SIGKILL signal\n");\r
228                                 goto swtd_release_l1;\r
229                         }\r
230                         if ( sigismember(&current->parent->blocked, SIGCHLD) ) {\r
231                                 dbg_printk("swtd_release get SIGCHLD signal\n");\r
232                                 goto swtd_release_l1;\r
233                         }\r
234                         if ( sigismember(&current->blocked, SIGCHLD) ) {\r
235                                 dbg_printk("swtd_release get SIGCHLD signal\n");\r
236                                 goto swtd_release_l1;\r
237                         }\r
238                         if ( sigismember(&current->parent->blocked, SIGTERM) ) {\r
239                                 dbg_printk("swtd_release get SIGTERM signal\n");\r
240                                 goto swtd_release_l1;\r
241                         }\r
242                         if ( sigismember(&current->blocked, SIGTERM) ) {\r
243                                 dbg_printk("swtd_release get SIGTERM signal\n");\r
244                                 goto swtd_release_l1;\r
245                         }\r
246                         if ( sigismember(&current->parent->blocked, SIGINT) ) {\r
247                                 dbg_printk("swtd_release get SIGINT signal\n");\r
248                                 goto swtd_release_l1;\r
249                         }\r
250                         if ( sigismember(&current->blocked, SIGINT) ) {\r
251                                 dbg_printk("swtd_release get SIGINT signal\n");\r
252                                 goto swtd_release_l1;\r
253                         }\r
254 #ifdef VICTOR_DEBUG     // add by Victor Yu. 02-23-2006\r
255                         if ( sigismember(&current->parent->blocked, SIGSTOP) ) {\r
256                                 dbg_printk("swtd_release get SIGSTOP signal\n");\r
257                         }\r
258                         if ( sigismember(&current->parent->blocked, SIGHUP) ) {\r
259                                 dbg_printk("swtd_release get SIGHUP signal\n");\r
260                         }\r
261                         if ( sigismember(&current->parent->blocked, SIGQUIT) ) {\r
262                                 dbg_printk("swtd_release get SIGQUIT signal\n");\r
263                         }\r
264                         if ( sigismember(&current->parent->blocked, SIGTSTP) ) {\r
265                                 dbg_printk("swtd_release get SIGTSTP signal\n");\r
266                         }\r
267                         if ( sigismember(&current->parent->blocked, SIGABRT) ) {\r
268                                 dbg_printk("swtd_release get SIGABRT signal\n");\r
269                         }\r
270                         if ( sigismember(&current->parent->blocked, SIGSEGV) ) {\r
271                                 dbg_printk("swtd_release get SIGSEGV signal\n");\r
272                         }\r
273 #endif\r
274                 } else {        // normal close the file handle\r
275 swtd_release_l1:\r
276 #endif\r
277                         if ( swtduserenabled ) {\r
278                                 swtd_disable();\r
279                                 del_timer(&swtdtimer);\r
280                                 swtduserenabled = 0;\r
281 #if 0   // mask by Victor Yu. 05-15-2006\r
282                                 swtdtime = DEFAULT_WATCHDOG_TIME;\r
283                                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
284                                 add_timer(&swtdtimer);\r
285                                 swtd_enable();\r
286 #endif\r
287                         }\r
288 #if 0   // mask by Victor Yu. 02-23-2006\r
289                 }\r
290 #endif\r
291                 opencounts = 0;\r
292         }\r
293 #if 0   // mask by Victor Yu. 05-15-1006\r
294         spin_unlock(&swtdlock);\r
295 #else   // add by Victor Yu. 05-15-2006\r
296         restore_flags(flags);\r
297 #endif\r
298         return 0;\r
301 static void swtd_reboot(void *unused)\r
303         char    *argv[2], *envp[5];\r
305         if ( in_interrupt() )\r
306                 return;\r
307         if ( !current->fs->root )\r
308                 return;\r
309         argv[0] = "/sbin/reboot";\r
310         argv[1] = 0;\r
311         envp[0] = "HOME=/";\r
312         envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";\r
313         envp[2] = 0;\r
314         call_usermodehelper(argv[0], argv, envp, 0);\r
317 static void swtd_poll(unsigned long ignore)\r
319         unsigned long   flags;\r
321 #if 0   // mask by Victor Yu. 05-15-2006\r
322         spin_lock(&swtdlock);\r
323 #else   // add by Victor Yu. 05-15-2006\r
324         save_flags(flags);\r
325         cli();\r
326 #endif\r
327         swtd_disable();\r
328         del_timer(&swtdtimer);\r
329 #if 0   // mask by Victor Yu. 05-15-2006\r
330         if ( swtduserenabled ) {\r
331                 dbg_printk("Now reboot the system.\n");\r
332                 schedule_work(&rebootqueue);\r
333         } else {\r
334                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
335                 add_timer(&swtdtimer);\r
336                 swtd_enable();\r
337         }\r
338         spin_unlock(&swtdlock);\r
339 #else   // add by Victor Yu. 05-15-2006\r
340         dbg_printk("Now reboot the system.\n");\r
341         schedule_work(&rebootqueue);\r
342         restore_flags(flags);\r
343 #endif\r
346 static int swtd_proc_output(char *buf)\r
348         char    *p;\r
350         p = buf;\r
351         p += sprintf(p,\r
352                      "user enable\t: %d\n"\r
353                      "ack time\t: %d msec\n",\r
354                      swtduserenabled, (int)swtdtime);\r
355         return p - buf;\r
358 static int swtd_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)\r
360         int     len=swtd_proc_output(page);\r
362         if ( len <= off + count )\r
363                 *eof = 1;\r
364         *start = page + off;\r
365         len -= off;\r
366         if ( len > count )\r
367                 len = count;\r
368         if ( len < 0 )\r
369                 len = 0;\r
370         return len;\r
373 static struct file_operations swtd_fops = {\r
374         owner:THIS_MODULE,\r
375         llseek:NULL,\r
376         ioctl:swtd_ioctl,\r
377         open:swtd_open,\r
378         release:swtd_release,\r
379 };\r
380 static struct miscdevice swtd_dev = {\r
381         MOXA_WATCHDOG_MINOR,\r
382         "swtd",\r
383         &swtd_fops\r
384 };\r
386 static void __exit moxa_swtd_exit(void)\r
388         unsigned long   flags;\r
390 #if 0   // mask by Victor Yu. 05-15-2006\r
391         spin_lock(&swtdlock);\r
392 #else   // add by Victor Yu. 05-15-2006\r
393         save_flags(flags);\r
394         cli();\r
395 #endif\r
396         if ( swtduserenabled ) {\r
397                 swtd_disable();\r
398                 del_timer(&swtdtimer);\r
399                 swtduserenabled = 0;\r
400                 opencounts = 0;\r
401         }\r
402 #if 0   // mask by Victor Yu. 05-15-2006\r
403         spin_unlock(&swtdlock);\r
404 #else   // add by Victor Yu. 05-15-2006\r
405         restore_flags(flags);\r
406 #endif\r
407         misc_deregister(&swtd_dev);\r
410 static int __init moxa_swtd_init(void)\r
412         // register misc\r
413         if ( misc_register(&swtd_dev) ) {\r
414                 printk("Moxa ART CPU WatchDog: Register misc fail !\n");\r
415                 goto moxa_swtd_init_err;\r
416         }\r
417         INIT_WORK(&rebootqueue, swtd_reboot, NULL);\r
418 #if 0   // mask by Victor Yu. 05-15-2006\r
419         spin_lock(&swtdlock);\r
420 #endif\r
421         init_timer(&swtdtimer);\r
422         swtdtimer.function = swtd_poll;\r
423 #if 0   // mask by Victor Yu. 05-15-2006\r
424         swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
425         add_timer(&swtdtimer);\r
426         swtd_enable();\r
427         spin_unlock(&swtdlock);\r
428 #endif\r
429         create_proc_read_entry("driver/swtd", 0, 0, swtd_read_proc, NULL);\r
430         printk("Moxa ART CPU WatchDog driver v" MOXA_WATCHDOG_VERSION "\n");\r
432         return 0;\r
434 moxa_swtd_init_err:\r
435         misc_deregister(&swtd_dev);\r
436         return -ENOMEM;\r
439 module_init(moxa_swtd_init);\r
440 module_exit(moxa_swtd_exit);\r
442 MODULE_AUTHOR("Victor Yu");\r
443 MODULE_LICENSE("GPL");\r