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.bak-05122006
blob5beae796cbfaf0a5945ab04ff1877d0fe562f6e3
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 static spinlock_t               swtdlock=SPIN_LOCK_UNLOCKED;\r
52 static int                      opencounts=0;\r
53 static int                      swtduserenabled=0;\r
54 static unsigned long            swtdtime=DEFAULT_WATCHDOG_TIME;\r
55 static struct timer_list        swtdtimer;\r
56 static struct work_struct       rebootqueue;\r
58 static void swtd_enable(void)\r
59 {\r
60         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+4) = WATCHDOG_COUNTER(swtdtime+WATCHDOG_TOL_COUNT_TIME);\r
61         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+8) = 0x5ab9;\r
62         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0x03;\r
63 }\r
65 static void swtd_disable(void)\r
66 {\r
67         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0;\r
68 }\r
70 #define IOCTL_WATCHDOG_ENABLE           1       // enable watch dog and set time (unint msec)\r
71 #define IOCTL_WATCHDOG_DISABLE          2       // disable watch dog, kernle do it\r
72 #define IOCTL_WATCHDOG_GET_SETTING      3       // get now setting about mode and time\r
73 #define IOCTL_WATCHDOG_ACK              4       // to ack watch dog\r
74 struct swtd_set_struct {\r
75         int             mode;\r
76         unsigned long   time;\r
77 };\r
78 static int swtd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)\r
79 {\r
80         unsigned long           time;\r
81         struct swtd_set_struct  nowset;\r
83         switch ( cmd ) {\r
84         case IOCTL_WATCHDOG_ENABLE :\r
85                 if ( copy_from_user(&time, (void *)arg, sizeof(time)) )\r
86                         return -EFAULT;\r
87                 if ( time < WATCHDOG_MIN_TIME || time > WATCHDOG_MAX_TIME )\r
88                         return -EINVAL;\r
89                 spin_lock(&swtdlock);\r
90                 swtd_disable();\r
91                 del_timer(&swtdtimer);\r
92                 swtdtime = time;\r
93                 swtduserenabled = 1;\r
94                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
95                 add_timer(&swtdtimer);\r
96                 swtd_enable();\r
97                 spin_unlock(&swtdlock);\r
98                 break;\r
99         case IOCTL_WATCHDOG_DISABLE :\r
100                 spin_lock(&swtdlock);\r
101                 if ( swtduserenabled ) {\r
102                         swtd_disable();\r
103                         del_timer(&swtdtimer);\r
104                         swtduserenabled = 0;\r
105                         swtdtime = DEFAULT_WATCHDOG_TIME;\r
106                         swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
107                         add_timer(&swtdtimer);\r
108                         swtd_enable();\r
109                 }\r
110                 spin_unlock(&swtdlock);\r
111                 break;\r
112         case IOCTL_WATCHDOG_GET_SETTING :\r
113                 nowset.mode = swtduserenabled;\r
114                 nowset.time = swtdtime;\r
115                 if ( copy_to_user((void *)arg, &nowset, sizeof(nowset)) )\r
116                         return -EFAULT;\r
117                 break;\r
118         case IOCTL_WATCHDOG_ACK :\r
119                 spin_lock(&swtdlock);\r
120                 if ( swtduserenabled ) {\r
121                         swtd_disable();\r
122                         del_timer(&swtdtimer);\r
123                         swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
124                         add_timer(&swtdtimer);\r
125                         swtd_enable();\r
126                 }\r
127                 spin_unlock(&swtdlock);\r
128                 break;\r
129         default:\r
130                 return -EINVAL;\r
131         }\r
132         return 0;\r
135 static int swtd_open(struct inode *inode, struct file *file)\r
137         if ( MINOR(inode->i_rdev) != MOXA_WATCHDOG_MINOR )\r
138                 return -ENODEV;\r
139         spin_lock(&swtdlock);\r
140         opencounts++;\r
141         spin_unlock(&swtdlock);\r
142         return 0;\r
145 static int swtd_release(struct inode *inode, struct file *file)\r
147         spin_lock(&swtdlock);\r
148         dbg_printk("swtd_release entry.\n");\r
149         opencounts--;\r
150         if ( opencounts <= 0 ) {\r
151 #if 0   // mask by Victor Yu. 02-23-2006\r
152                 if ( signal_pending(current) ) {\r
153                         dbg_printk("swtd_release has signal pending\n");\r
154                         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
155                         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
156                         if ( sigismember(&current->parent->blocked, SIGKILL) ) {\r
157                                 dbg_printk("swtd_release get SIGKILL signal\n");\r
158                                 goto swtd_release_l1;\r
159                         }\r
160                         if ( sigismember(&current->blocked, SIGKILL) ) {\r
161                                 dbg_printk("swtd_release get SIGKILL signal\n");\r
162                                 goto swtd_release_l1;\r
163                         }\r
164                         if ( sigismember(&current->parent->blocked, SIGCHLD) ) {\r
165                                 dbg_printk("swtd_release get SIGCHLD signal\n");\r
166                                 goto swtd_release_l1;\r
167                         }\r
168                         if ( sigismember(&current->blocked, SIGCHLD) ) {\r
169                                 dbg_printk("swtd_release get SIGCHLD signal\n");\r
170                                 goto swtd_release_l1;\r
171                         }\r
172                         if ( sigismember(&current->parent->blocked, SIGTERM) ) {\r
173                                 dbg_printk("swtd_release get SIGTERM signal\n");\r
174                                 goto swtd_release_l1;\r
175                         }\r
176                         if ( sigismember(&current->blocked, SIGTERM) ) {\r
177                                 dbg_printk("swtd_release get SIGTERM signal\n");\r
178                                 goto swtd_release_l1;\r
179                         }\r
180                         if ( sigismember(&current->parent->blocked, SIGINT) ) {\r
181                                 dbg_printk("swtd_release get SIGINT signal\n");\r
182                                 goto swtd_release_l1;\r
183                         }\r
184                         if ( sigismember(&current->blocked, SIGINT) ) {\r
185                                 dbg_printk("swtd_release get SIGINT signal\n");\r
186                                 goto swtd_release_l1;\r
187                         }\r
188 #ifdef VICTOR_DEBUG     // add by Victor Yu. 02-23-2006\r
189                         if ( sigismember(&current->parent->blocked, SIGSTOP) ) {\r
190                                 dbg_printk("swtd_release get SIGSTOP signal\n");\r
191                         }\r
192                         if ( sigismember(&current->parent->blocked, SIGHUP) ) {\r
193                                 dbg_printk("swtd_release get SIGHUP signal\n");\r
194                         }\r
195                         if ( sigismember(&current->parent->blocked, SIGQUIT) ) {\r
196                                 dbg_printk("swtd_release get SIGQUIT signal\n");\r
197                         }\r
198                         if ( sigismember(&current->parent->blocked, SIGTSTP) ) {\r
199                                 dbg_printk("swtd_release get SIGTSTP signal\n");\r
200                         }\r
201                         if ( sigismember(&current->parent->blocked, SIGABRT) ) {\r
202                                 dbg_printk("swtd_release get SIGABRT signal\n");\r
203                         }\r
204                         if ( sigismember(&current->parent->blocked, SIGSEGV) ) {\r
205                                 dbg_printk("swtd_release get SIGSEGV signal\n");\r
206                         }\r
207 #endif\r
208                 } else {        // normal close the file handle\r
209 swtd_release_l1:\r
210 #endif\r
211                         if ( swtduserenabled ) {\r
212                                 swtd_disable();\r
213                                 del_timer(&swtdtimer);\r
214                                 swtduserenabled = 0;\r
215                                 swtdtime = DEFAULT_WATCHDOG_TIME;\r
216                                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
217                                 add_timer(&swtdtimer);\r
218                                 swtd_enable();\r
219                         }\r
220 #if 0   // mask by Victor Yu. 02-23-2006\r
221                 }\r
222 #endif\r
223                 opencounts = 0;\r
224         }\r
225         spin_unlock(&swtdlock);\r
226         return 0;\r
229 static void swtd_reboot(void *unused)\r
231         char    *argv[2], *envp[5];\r
233         if ( in_interrupt() )\r
234                 return;\r
235         if ( !current->fs->root )\r
236                 return;\r
237         argv[0] = "/sbin/reboot";\r
238         argv[1] = 0;\r
239         envp[0] = "HOME=/";\r
240         envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";\r
241         envp[2] = 0;\r
242         call_usermodehelper(argv[0], argv, envp, 0);\r
245 #define VICTOR_TEMP_CODE\r
246 static void swtd_poll(unsigned long ignore)\r
248         spin_lock(&swtdlock);\r
249 #ifndef VICTOR_TEMP_CODE\r
250         swtd_disable();\r
251 #endif\r
252         del_timer(&swtdtimer);\r
253         if ( swtduserenabled ) {\r
254                 dbg_printk("Now reboot the system.\n");\r
255                 schedule_work(&rebootqueue);\r
256         } else {\r
257 #ifdef VICTOR_TEMP_CODE\r
258                 swtd_disable();\r
259 #endif\r
260                 swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
261                 add_timer(&swtdtimer);\r
262                 swtd_enable();\r
263         }\r
264         spin_unlock(&swtdlock);\r
267 static int swtd_proc_output(char *buf)\r
269         char    *p;\r
271         p = buf;\r
272         p += sprintf(p,\r
273                      "user enable\t: %d\n"\r
274                      "ack time\t: %d msec\n",\r
275                      swtduserenabled, (int)swtdtime);\r
276         return p - buf;\r
279 static int swtd_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)\r
281         int     len=swtd_proc_output(page);\r
283         if ( len <= off + count )\r
284                 *eof = 1;\r
285         *start = page + off;\r
286         len -= off;\r
287         if ( len > count )\r
288                 len = count;\r
289         if ( len < 0 )\r
290                 len = 0;\r
291         return len;\r
294 static struct file_operations swtd_fops = {\r
295         owner:THIS_MODULE,\r
296         llseek:NULL,\r
297         ioctl:swtd_ioctl,\r
298         open:swtd_open,\r
299         release:swtd_release,\r
300 };\r
301 static struct miscdevice swtd_dev = {\r
302         MOXA_WATCHDOG_MINOR,\r
303         "swtd",\r
304         &swtd_fops\r
305 };\r
307 static void __exit moxa_swtd_exit(void)\r
309         spin_lock(&swtdlock);\r
310         swtd_disable();\r
311         del_timer(&swtdtimer);\r
312         spin_unlock(&swtdlock);\r
313         misc_deregister(&swtd_dev);\r
316 static int __init moxa_swtd_init(void)\r
318         // register misc\r
319         if ( misc_register(&swtd_dev) ) {\r
320                 printk("Moxa ART CPU WatchDog: Register misc fail !\n");\r
321                 goto moxa_swtd_init_err;\r
322         }\r
323         *(volatile unsigned int *)(CPE_PMU_VA_BASE + 0x10) |= 0x20;\r
324         INIT_WORK(&rebootqueue, swtd_reboot, NULL);\r
325         spin_lock(&swtdlock);\r
326         init_timer(&swtdtimer);\r
327         swtdtimer.function = swtd_poll;\r
328         swtdtimer.expires = jiffies + WATCHDOG_JIFFIES(swtdtime);\r
329         add_timer(&swtdtimer);\r
330         swtd_enable();\r
331         spin_unlock(&swtdlock);\r
332         create_proc_read_entry("driver/swtd", 0, 0, swtd_read_proc, NULL);\r
333         printk("Moxa ART CPU WatchDog driver v" MOXA_WATCHDOG_VERSION "\n");\r
335         return 0;\r
337 moxa_swtd_init_err:\r
338         misc_deregister(&swtd_dev);\r
339         return -ENOMEM;\r
342 module_init(moxa_swtd_init);\r
343 module_exit(moxa_swtd_exit);\r
345 MODULE_AUTHOR("Victor Yu");\r
346 MODULE_LICENSE("GPL");\r