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-wade
bloba221ad9b6b17356c116aa2ab6d9106382849f2d8
1 /*\r
2  * This is Moxa UC7000 watch dog driver for CV case.\r
3  *\r
4  * History:\r
5  * Date         Aurhor                  Comment\r
6  * 09-15-2004   Victor Yu.              Create it. I name it sWatchDog\r
7  * 06-10-2005   Jared Wu.                       Fix the watch dog devic driver bug. kernel_thread() is a system call cannot be invoke in timer timeout interrupt. I move it to the moxa_swtd_init()\r
8  * 06-18-2009   Wade Liang              Change to interrupt mode for flash problem\r
9  */\r
10 #define __KERNEL_SYSCALLS__\r
11 #include <linux/config.h>\r
12 #include <linux/proc_fs.h> /* Necessary because we use the proc fs */\r
13 #include <linux/version.h>\r
14 #include <linux/unistd.h>\r
15 #include <linux/string.h>\r
16 #include <linux/ctype.h>\r
17 #include <linux/module.h>\r
18 #include <linux/kernel.h>\r
19 #include <linux/miscdevice.h>\r
20 #include <linux/fcntl.h>\r
21 #include <linux/init.h>\r
22 #include <linux/poll.h>\r
23 #include <linux/spinlock.h>\r
24 #include <linux/delay.h>\r
25 #include <linux/timer.h>\r
26 #include <linux/ioport.h>\r
27 #include <linux/interrupt.h>\r
28 #include <linux/sched.h>\r
29 #include <linux/signal.h>\r
30 #include <linux/mm.h>\r
31 #include <asm/io.h>\r
32 #include <asm/uaccess.h>\r
33 #include <asm/system.h>\r
34 #include <asm/irq.h>\r
35 #include <asm/bitops.h>\r
36 #include <linux/reboot.h>\r
37 #include <asm/types.h>\r
38 #include <linux/notifier.h>\r
39 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)\r
40 #include <linux/workqueue.h>\r
41 #endif\r
43 #define CONFIG_ARCH_MOXACPU  // define for IA-24X/W3X1/W3X5 serials\r
45 #if defined (DA68X)\r
46 #include "x86_moxa_swtd.h"\r
47 #elif defined (CONFIG_CPU_IXP43X) || defined (ARCH_IXDP425) || defined (ARCH_IXDP422)\r
48 #include "ixp43x_moxa_swtd.h"\r
49 #elif defined(CONFIG_ARCH_MOXACPU)\r
50 #include "moxaart_moxa_swtd.h"\r
51 #elif defined(CONFIG_MACH_MOXA_IA261) || defined(CONFIG_MACH_MOXA_W406)\r
52 #include "ep93xx_moxa_swtd.h"\r
53 #endif\r
54 #ifndef WATCHDOG_NOWAYOUT\r
55 #define WATCHDOG_NOWAYOUT 0\r
56 #endif\r
58 static struct proc_dir_entry *swtd_proc_file;\r
59 static int                      opencounts=0;\r
60 static int                      swtduserenabled=0;\r
61 static unsigned long            swtdtime=DEFAULT_WATCHDOG_TIME;\r
62 static struct timer_list        timer_swtd;\r
63 static int                      bswtd_timeout = 0;\r
64 static int                      nowayout = WATCHDOG_NOWAYOUT;\r
65 static int                      debug = 0;\r
66 static char                     expect_close;\r
67 static spinlock_t               swtd_lock=SPIN_LOCK_UNLOCKED;\r
68 static int irq_res=-1;\r
69 static unsigned long flags=0;\r
70 #if defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
71 static int                      pollcnt=0;\r
72 #endif\r
73 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)\r
74 static wait_queue_head_t swtd_queue; \r
75 #endif\r
77 // add by Jared Wu. 03-10-2009 Ack the watchdog instead of disable it.\r
78 // this means that if the kernel crashed, the hardware watchdog will\r
79 // reset in 15 sec.\r
80 static void swtd_ack(unsigned long swtd_ack_time)\r
81 {\r
82         if (debug)\r
83                 printk("<1>swtd_ack: swtd_time=%lu\n",swtd_ack_time);\r
84 #ifndef __WDT_TEST__\r
85 #if defined (DA68X)\r
86         superio_enter_config();\r
87         superio_set_logic_device(8);    //logic device 8\r
88         superio_set_reg((swtd_ack_time/1000), 0xF6);    //Reg:F6,30 sec\r
89 #elif defined (CONFIG_CPU_IXP43X) || defined (ARCH_IXDP425)\r
90         *IXP4XX_OSWK = IXP4XX_WDT_KEY;\r
91         *IXP4XX_OSWE = 0x0;\r
92         *IXP4XX_OSWT = WATCHDOG_COUNTER(swtd_ack_time);\r
93         *IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE;\r
94         *IXP4XX_OSWK = 0;\r
95 #elif defined (ARCH_IXDP422)\r
96         *IXP425_OSWK = 0x482e;\r
97         *IXP425_OSWE = 0x0;\r
98         *IXP425_OSWT = WATCHDOG_COUNTER(swtd_ack_time);\r
99         *IXP425_OSWE = IXP4XX_WDOG_CNT_ENA | IXP4XX_WDOG_RST_ENA;\r
100         *IXP425_OSWK = 0;\r
101 #elif defined(CONFIG_ARCH_MOXACPU)\r
102         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+4) = WATCHDOG_COUNTER(swtd_ack_time);\r
103         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+8) = 0x5ab9;\r
104         /* Change to interrupt mode 0x05, reset mode is 0x03*/\r
105         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0x05;\r
106 #elif defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
107         // Not used\r
108 #endif\r
109 #endif\r
112 static void swtd_enable(void)\r
114         if (debug)\r
115                 printk("swtd_enable: swtdtime=%lu\n",swtdtime+WATCHDOG_DEFER_TIME);\r
116 #ifndef __WDT_TEST__\r
117 #if defined (DA68X)\r
118         superio_enter_config();\r
119         superio_set_logic_device(8);    //logic device 8\r
120         superio_set_reg(1, 0x30);       //Reg:30 active WDT\r
121         superio_set_reg(0, 0xF5);       //Reg:F5\r
122         superio_set_reg(0, 0xF7);       //Reg:F7\r
123         superio_set_reg((swtdtime+WATCHDOG_DEFER_TIME)/1000, 0xF6);     //Reg:F6,30 sec\r
124 #elif defined (CONFIG_CPU_IXP43X) || defined (ARCH_IXDP425)\r
125         *IXP4XX_OSWK = IXP4XX_WDT_KEY;\r
126         *IXP4XX_OSWE = 0x0;\r
127         *IXP4XX_OSWT = WATCHDOG_COUNTER(swtdtime+WATCHDOG_DEFER_TIME);\r
128         *IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE;\r
129         *IXP4XX_OSWK = 0;\r
130 #elif defined (ARCH_IXDP422)\r
131         *IXP425_OSWK = 0x482e;\r
132         *IXP425_OSWE = 0x0;\r
133         *IXP425_OSWT = WATCHDOG_COUNTER(swtdtime+WATCHDOG_DEFER_TIME);\r
134         //*IXP425_OSWE = IXP4XX_WDOG_CNT_ENA | IXP4XX_WDOG_INT_ENA | IXP4XX_WDOG_RST_ENA ;\r
135         *IXP425_OSWE = IXP4XX_WDOG_CNT_ENA | IXP4XX_WDOG_RST_ENA ;\r
136         *IXP425_OSWK = 0;\r
137 #elif defined(CONFIG_ARCH_MOXACPU)\r
138         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+4) = WATCHDOG_COUNTER(swtdtime+WATCHDOG_DEFER_TIME);\r
139         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+8) = 0x5ab9;\r
140         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0x03;\r
141 #elif defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
142         __raw_writew(0x5555, EP93XX_WATCHDOG_BASE);     // reset watchdog counter\r
143         __raw_writew(0xaaaa, EP93XX_WATCHDOG_BASE);     // enable watchdog\r
144 #endif\r
145 #endif\r
148 static void swtd_disable(void)\r
150         if (debug)\r
151                 printk("swtd_disable\n");\r
152 #ifndef __WDT_TEST__\r
153 #if defined (DA68X)\r
154         superio_enter_config();\r
155         superio_set_logic_device(8);    //logic device 8\r
156         superio_set_reg(0, 0xF6);       //Reg:F6 counter register\r
157 #elif defined (CONFIG_CPU_IXP43X) || defined (ARCH_IXDP425)\r
158         *IXP4XX_OSWK = IXP4XX_WDT_KEY;\r
159         *IXP4XX_OSWE = 0;\r
160         *IXP4XX_OSWK = 0;\r
161 #elif defined (ARCH_IXDP422)\r
162         *IXP425_OSWK = 0x482e;\r
163         *IXP425_OSWE = 0;\r
164         *IXP425_OSWK = 0;\r
165 #elif defined(CONFIG_ARCH_MOXACPU)\r
166         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0;\r
167 #elif defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
168         __raw_writew(0xaa55, EP93XX_WATCHDOG_BASE);\r
169 #endif\r
170 #endif\r
173 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)\r
174 static void swtd_reboot(void *unused)\r
176         char    *argv[2], *envp[5];\r
178         if ( in_interrupt() )\r
179                 return;\r
180         if ( !current->fs->root )\r
181                 return;\r
182 /*\r
183         argv[0] = "/bin/reboot";\r
184         argv[1] = 0;\r
185 */\r
186         argv[0] = "/sbin/reboot";\r
187         argv[1] = 0;\r
188         envp[0] = "HOME=/";\r
189         envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";\r
190         envp[2] = 0;\r
191         call_usermodehelper(argv[0], argv, envp, 0);\r
193 #else\r
194 static char     *argv_init[2]={"reboot", NULL};\r
195 static char     *envp_init[3]={"HOME=/", "TERM=linux", NULL};\r
197 static int swtd_reboot(void *unused)\r
199         interruptible_sleep_on(&swtd_queue);\r
200         printk("<1>exec reboot.\n");\r
201         execve("/sbin/reboot", argv_init, envp_init);\r
202         return 0;\r
204 #endif\r
206 #if defined (DA68X) || defined (ARCH_IXDP425) || defined(CONFIG_ARCH_MOXACPU)\r
207 DECLARE_WORK(rebootqueue, swtd_reboot, NULL);\r
208 #elif defined (CONFIG_CPU_IXP43X) || defined(CONFIG_MACH_MOXA_IA261) || defined(CONFIG_MACH_MOXA_W406)\r
209 DECLARE_WORK(rebootqueue, swtd_reboot);\r
210 #endif\r
212 static void swtd_poll(unsigned long ignore)\r
214         spin_lock(&swtd_lock);\r
215 #if defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
216         if ( pollcnt++ <= (swtdtime/WATCHDOG_ACK_JIFFIES) ) {\r
217                 timer_swtd.expires = jiffies + WATCHDOG_ACK_JIFFIES;\r
218                 add_timer(&timer_swtd);\r
219                 swtd_enable();\r
220                 spin_unlock(&swtd_lock);\r
221                 return ;\r
222         }\r
223         swtd_enable();\r
224         if (debug)\r
225                 printk("<1>swtd_poll: Now reboot the system.\n");\r
226         #ifndef __WDT_TEST__\r
227         schedule_work(&rebootqueue);\r
228         #endif\r
229         bswtd_timeout = 1;\r
230 #else  // (CONFIG_CPU_IXP43X) || defined(CONFIG_MACH_MOXA_IA261) || defined(CONFIG_MACH_MOXA_W406) || defined(CONFIG_ARCH_MOXACPU)\r
232         if ( swtduserenabled ) {\r
233                 swtd_ack(WATCHDOG_DEFER_TIME);\r
234                 if (debug)\r
235                         printk("<1>swtd_poll: Now reboot the system.\n");\r
236         #ifndef __WDT_TEST__\r
237                 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)\r
238                 schedule_work(&rebootqueue);\r
239                 #else\r
240                 /* Jared said, awake any swtd_reboot kernel thread */\r
241                 wake_up_interruptible(&swtd_queue);\r
242                 #endif\r
243         #endif\r
244                 bswtd_timeout = 1;\r
245         }\r
246         else\r
247         {\r
248                 if (debug)\r
249                         printk("swtd_poll: ack the hardware watchdog timer\n");\r
250         /* 1-17-2005 Jared said, if disable the watchdog, \r
251          * it would not reboot while the system is very busy \r
252          */\r
253                 timer_swtd.expires = jiffies +WATCHDOG_ACK_JIFFIES(swtdtime);\r
254                 add_timer(&timer_swtd);\r
255                 swtd_ack(swtdtime+WATCHDOG_DEFER_TIME);\r
256         }\r
257 #endif\r
258         spin_unlock(&swtd_lock);\r
261 ssize_t moxaswtd_proc_read(char *buffer,char **buffer_location,off_t offset, int buffer_length, int *eof, void *data)\r
263     int len=0; /* The number of bytes actually used */\r
264             \r
265     if (offset > 0) {\r
266       len =0;\r
267     }\r
268     else {\r
269         //Fill the buffer and get its length\r
270       len += sprintf(buffer+len,\r
271              "user enable\t: %d\n"\r
272              "ack time\t: %d msec\n"\r
273 #if defined (DA68X)\r
274              "hardware watchdog counter\t: %d sec\n"\r
275 #endif\r
276              ,swtduserenabled, (int)swtdtime \r
277 #if defined (DA68X)\r
278              ,superio_get_reg(0xF6)\r
279 #endif\r
280              );\r
281     }\r
282     return len;\r
285 static int swtd_open(struct inode *inode, struct file *file)\r
287         if ( MINOR(inode->i_rdev) != MOXA_WATCHDOG_MINOR )\r
288                 return -ENODEV;\r
289         \r
290         spin_lock_irq(&swtd_lock);\r
291 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)\r
292         if (nowayout)\r
293                 __module_get(THIS_MODULE);\r
294 #endif\r
295         bswtd_timeout = 0; // reset the timeout flag\r
296         opencounts++;\r
297         spin_unlock_irq(&swtd_lock);\r
299         return 0;\r
302 // Kernel ack the watchdog timer and reset the state machine\r
303 static void swtd_release_timer(void) {\r
304         swtdtime = DEFAULT_WATCHDOG_TIME;\r
305 #if defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
306         swtd_disable();\r
307         del_timer(&timer_swtd);\r
308 #else\r
309         mod_timer(&timer_swtd, jiffies + WATCHDOG_ACK_JIFFIES(swtdtime));\r
310         swtd_ack(swtdtime+WATCHDOG_DEFER_TIME);\r
311 #endif\r
312         swtduserenabled = 0;\r
313         bswtd_timeout = 0; // reset the timeout flag\r
317 static int swtd_release(struct inode *inode, struct file *file)\r
319 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)\r
320         sigset_t *sigset = &current->signal->shared_pending.signal;\r
321 #else\r
322         sigset_t *sigset = &current->pending.signal;\r
323 #endif\r
325         spin_lock_irq(&swtd_lock);\r
327         if (debug)\r
328                 printk("<1>swtd_release entry\n");\r
329         opencounts--;\r
331         if ( opencounts <= 0 ) {\r
332                 /*\r
333                  *      Shut off the timer.\r
334                  */\r
335                 if (expect_close == 42) {\r
336                         printk("<1>swtd_release: expect close\n");\r
337                         if ( !bswtd_timeout  ) {\r
338                                 swtd_release_timer();\r
339                         }\r
340                 }\r
341                 else if ( signal_pending(current) ) {\r
342                         if (debug)\r
343                                 printk("<1>swtd_release[%d] has signal pending\n",__LINE__);\r
344                         if ( sigismember (sigset, SIGKILL) || \\r
345                             sigismember (sigset, SIGINT) || \\r
346                             sigismember (sigset, SIGTERM) ) {\r
347                                 if (debug)\r
348                                         printk("<1>swtd_release[%d] get SIGKILL/SIGINT/SIGTERM signal\n",__LINE__);\r
349 #if 1 // For Taitung, 03-30-2009 should comment out this line\r
350                                 if ( !bswtd_timeout  ) {\r
351                                         swtd_release_timer();\r
352                                 }\r
353 #endif\r
354                         }\r
355 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)\r
356                 } else if ( current->signal->group_exit_code == SIGQUIT || \\r
357                         current->signal->group_exit_code == SIGILL || \\r
358                         current->signal->group_exit_code == SIGABRT || \\r
359                         current->signal->group_exit_code == SIGFPE || \\r
360                         current->signal->group_exit_code == SIGSEGV ) {\r
361                         if (debug)\r
362                                 printk("<1>swtd_release[%d] got coredump\n",__LINE__);\r
363                 }\r
364 #else\r
365                 } else if ( sigismember (sigset, SIGILL) || \\r
366                             sigismember (sigset, SIGABRT) || \\r
367                             sigismember (sigset, SIGFPE) || \\r
368                             sigismember (sigset, SIGSEGV) ) {\r
369                             if (debug)\r
370                                 printk("<1>swtd_release[%d] got coredump\n",__LINE__);\r
371                         }\r
372 #endif\r
373                 else {  // normal close the file handle\r
374                         if (debug)\r
375                                 printk("<1>swtd_release_l1[%d] kernel ack the watchdog timer\n",__LINE__);\r
376                         if ( !bswtd_timeout  ) {\r
377 #if 1 // For Taitung, 03-30-2009 should comment out this line\r
378                                 swtd_release_timer();\r
379 #endif\r
380                         }\r
381                 }\r
382                 expect_close = 0;\r
383         }\r
384         spin_unlock_irq(&swtd_lock);\r
386         return 0;\r
389 static int swtd_ioctl (struct inode *inode, struct file *file, unsigned int ioc_cmd, unsigned long arg)\r
391         unsigned long           time;\r
392         struct swtd_set_struct  nowset;\r
394         switch ( ioc_cmd ) {\r
395         case IOCTL_WATCHDOG_ENABLE :\r
396                 if ( copy_from_user(&time, (unsigned long *)arg, sizeof(unsigned long)) )\r
397                         return -EFAULT;\r
398                 if ( time < WATCHDOG_MIN_TIME || time > WATCHDOG_MAX_TIME )\r
399                         return -EINVAL;\r
400                 spin_lock_irq(&swtd_lock);\r
401                 if ( !bswtd_timeout ) {\r
402                         // Switch to user mode watchdog.\r
403                         // When the kernel timer timeout, the system will reboot\r
404                         swtduserenabled = 1;\r
405                         swtdtime = time;\r
406 #if defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
407                         pollcnt = 0;\r
408                         swtd_enable();\r
409                         if(timer_pending(&timer_swtd))\r
410                                 mod_timer(&timer_swtd, jiffies + WATCHDOG_ACK_JIFFIES);\r
411                         else\r
412                                 add_timer(&timer_swtd);\r
413 #else\r
414                         mod_timer(&timer_swtd, jiffies + WATCHDOG_ACK_JIFFIES(swtdtime));\r
415                         swtd_ack(swtdtime+WATCHDOG_DEFER_TIME);\r
416 #endif\r
417                 }\r
418                 spin_unlock_irq(&swtd_lock);\r
419                 break;\r
420         case IOCTL_WATCHDOG_DISABLE :\r
421                 spin_lock_irq(&swtd_lock);\r
422                 if ( swtduserenabled && !bswtd_timeout ) {\r
423 #if defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
424                         pollcnt=0;\r
425                         swtd_disable();\r
426                         del_timer(&timer_swtd);\r
427 #else\r
428                         // Switch to kernel mode watchdog.\r
429                         // The kernel timer will acknowledge the HW watchdog\r
430                         swtd_enable();\r
431                         mod_timer(&timer_swtd, jiffies + WATCHDOG_ACK_JIFFIES(swtdtime));\r
432 #endif\r
433                         swtdtime = DEFAULT_WATCHDOG_TIME;\r
434                         bswtd_timeout = 0; // reset the timeout flag\r
435                         swtduserenabled = 0;\r
436                 }\r
437                 spin_unlock_irq(&swtd_lock);\r
438                 break;\r
439         case IOCTL_WATCHDOG_GET_SETTING :\r
440                 nowset.mode = swtduserenabled;\r
441                 nowset.time = swtdtime;\r
442                 if ( copy_to_user((void *)arg, &nowset, sizeof(nowset)) )\r
443                         return -EFAULT;\r
444                 break;\r
445         case IOCTL_WATCHDOG_ACK :\r
446                 spin_lock_irq(&swtd_lock);\r
447                 if ( swtduserenabled && !bswtd_timeout ) {\r
448 #if defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
449                         pollcnt=0;\r
450                         swtd_enable();\r
451                         mod_timer(&timer_swtd, jiffies + WATCHDOG_ACK_JIFFIES);\r
452 #else\r
453                         // Switch to user mode watchdog.\r
454                         // When the kernel timer timeout, the system will reboot\r
455                         mod_timer(&timer_swtd, jiffies + WATCHDOG_ACK_JIFFIES(swtdtime));\r
456                         swtd_ack(swtdtime+WATCHDOG_DEFER_TIME);\r
457 #endif\r
458                 }\r
459                 spin_unlock_irq(&swtd_lock);\r
461                 break;\r
462         default:\r
463                 return -EINVAL;\r
464         }\r
465         return 0;\r
468 /*\r
469  *      swtd_write:\r
470  *      @file: file handle to the watchdog\r
471  *      @buf: buffer to write (unused as data does not matter here\r
472  *      @count: count of bytes\r
473  *      @ppos: pointer to the position to write. No seeks allowed\r
474  *\r
475  *      A write to a watchdog device is defined as a keepalive signal. Any\r
476  *      write of data will do, as we we don't define content meaning.\r
477  */\r
479 static ssize_t swtd_write(struct file *file, const char *buf, \\r
480                             size_t count, loff_t *ppos)\r
482         if (count)\r
483         {\r
484                 if (!nowayout)\r
485                 {\r
486                         size_t i;\r
488                         /* In case it was set long ago */\r
489 #if 0 // Comment out by Jared 2009-05-11, don't reset the expect_close flag\r
490                         expect_close = 0;\r
491 #endif\r
492                         for (i = 0; i != count; i++)\r
493                         {\r
494                                 char c;\r
495                                 if (get_user(c, buf + i))\r
496                                         return -EFAULT;\r
497                                 if (c == 'V')\r
498                                         expect_close = 42;\r
499                         }\r
500                 }\r
502                 /* someone wrote to us, we should restart timer */\r
503                 spin_lock_irq(&swtd_lock);\r
504                 if ( !bswtd_timeout ) {\r
505                         // Switch to user mode watchdog.\r
506                         // When the kernel timer timeout, the system will reboot\r
507                         swtduserenabled = 1;\r
508 #if defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
509                         pollcnt=0;\r
510                         mod_timer(&timer_swtd, jiffies + WATCHDOG_ACK_JIFFIES);\r
511                         swtd_enable();\r
512 #else\r
513                         mod_timer(&timer_swtd, jiffies + WATCHDOG_ACK_JIFFIES(swtdtime));\r
514                         swtd_ack(swtdtime+WATCHDOG_DEFER_TIME);\r
515 #endif\r
516                 }\r
517                 spin_unlock_irq(&swtd_lock);\r
519         }\r
520         return count;\r
523 /* IRQ handler: for hw watchdog and system reboot will call */\r
524 static irqreturn_t swtd_interrupt_handler(int irq, void *dev_id, struct pt_regs *regs)\r
526         /* Disable irq */\r
527         local_irq_save(flags);\r
528         if (debug)\r
529                 printk("swtd_interrupt_handler: enter\n");\r
531         /* Reset flash state */\r
532         *( volatile u16 *)(CPE_FLASH_VA_BASE+0x55*2) = 0xb0;//Suspend block erase\r
533         *( volatile u16 *)(CPE_FLASH_VA_BASE+0x55*2) = 0xff;//Change to read array mode\r
535         /* Call hardware reboot */\r
536         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+4) = 1;\r
537         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+8) = 0x5ab9;\r
538         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0x03;\r
540         while(1) ; \r
542         local_irq_restore(flags);\r
543         return IRQ_HANDLED;\r
544         \r
547 /* Panic hander */\r
548 static int swtd_panic_handler(struct notifier_block *this,\r
549                               unsigned long         event,\r
550                               void                  *unused)\r
551 {       \r
552         /* Avoid the software interrupt of swtd_ack */\r
553         spin_lock_bh(&swtd_lock);\r
555         if (debug) printk("swtd_panic_handler: enter\n");\r
557         /* Reset flash state */\r
558         if (debug) printk("swtd_panic_handler: reset flash state\n");\r
559         *( volatile u16 *)(CPE_FLASH_VA_BASE+0x55*2) = 0xb0;//Suspend block erase\r
560         *( volatile u16 *)(CPE_FLASH_VA_BASE+0x55*2) = 0xff;//Change to read array mode\r
562         /* Call hardware reboot */\r
563         if (debug) printk("swtd_panic_handler: call hardware rebooot\n");\r
564         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+4) = 1;\r
565         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+8) = 0x5ab9;\r
566         *(unsigned int *)(CPE_WATCHDOG_VA_BASE+12) = 0x03;\r
569         return NOTIFY_OK;\r
571         spin_unlock_bh(&swtd_lock);\r
574 /* Structure for notification */\r
575 static struct notifier_block swtd_panic_notifier = {\r
576         swtd_panic_handler,\r
577         NULL,\r
578         150   /* priority: INT_MAX >= x >= 0 */\r
579 };\r
581 static struct file_operations moxa_swtd_fops = {\r
582         .owner=THIS_MODULE,\r
583         .open=swtd_open,\r
584         .write=swtd_write,\r
585         .ioctl=swtd_ioctl,\r
586         .release=swtd_release,\r
587 };\r
589 static struct miscdevice wdt_miscdev = {\r
590         .minor = MOXA_WATCHDOG_MINOR,\r
591         .name = "swtd",\r
592         .fops = &moxa_swtd_fops,\r
593 };\r
595 static int __init moxaswtd_init(void) {\r
597         /* Register misc */\r
598         if ( misc_register(&wdt_miscdev)!=0 ) {\r
599                 printk("Moxa DA-681/662-LX WatchDog: Register misc fail !\n");\r
600                 goto moxa_swtd_init_err1;\r
601         }\r
603         /* Register IRQ handler */\r
604         irq_res= request_irq(IRQ_WATCHDOG, swtd_interrupt_handler,\r
605                         SA_INTERRUPT,"swtd_irq",\r
606                         NULL);\r
607         if (irq_res) {\r
608                 printk( KERN_EMERG "swtd: can't get assigned irq %i\n", IRQ_WATCHDOG);\r
609                 return -1;\r
610         }\r
611         \r
612         /* Resister panic handler */\r
613         notifier_chain_register(&panic_notifier_list, &swtd_panic_notifier);\r
615         /* Add timer */\r
616         init_timer(&timer_swtd);\r
617         timer_swtd.function=swtd_poll;\r
618 #if defined(CONFIG_MACH_MOXA_IA261)||defined(CONFIG_MACH_MOXA_W406)\r
619         timer_swtd.expires = jiffies + WATCHDOG_ACK_JIFFIES;\r
620 #else\r
621         timer_swtd.expires = jiffies + WATCHDOG_ACK_JIFFIES(swtdtime);\r
622         add_timer(&timer_swtd);\r
623         swtd_enable();\r
624 #endif\r
626         /* Create proc file */\r
627         swtd_proc_file = create_proc_read_entry("driver/swtd", 0644, NULL, moxaswtd_proc_read, NULL);\r
628         if ( !swtd_proc_file ) {\r
629                 printk("<1>moxaswtd_init:create_proc_read_entry() fail\n");\r
630                 goto moxa_swtd_init_err2;\r
631         }\r
633 #if defined (DA68X)\r
634         struct resource *base_res;\r
636         base_res = request_region(SUPERIO_CONFIG_PORT, 2, "swtd");\r
637         if (!base_res) {\r
638                 printk("<1>moxaswtd_init: can't get I/O address 0x%x\n", SUPERIO_CONFIG_PORT);\r
639                 goto moxa_swtd_init_err3;\r
640         }\r
641 #endif\r
643 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)\r
644         init_waitqueue_head  (&swtd_queue);\r
645         kernel_thread(swtd_reboot, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);\r
646 #endif\r
648         printk (KERN_INFO "initialized. (nowayout=%d)\n", nowayout);\r
649         printk (KERN_INFO "initialized. (debug=%d)\n", debug);\r
651         return 0;\r
653 #if defined (DA68X)\r
654 moxa_swtd_init_err3:\r
655         remove_proc_entry("driver/swtd", NULL);\r
656 #endif\r
657 moxa_swtd_init_err2:\r
658         if(timer_pending(&timer_swtd))\r
659                 del_timer(&timer_swtd);\r
660         misc_deregister(&wdt_miscdev);\r
661 moxa_swtd_init_err1:\r
662         return -ENOMEM;\r
665 static void __exit moxaswtd_exit(void) {\r
667 #if defined (DA68X)\r
668         release_region(SUPERIO_CONFIG_PORT, 2);\r
669         superio_exit_config();\r
670 #endif\r
671         remove_proc_entry("driver/swtd", NULL);\r
672         swtd_disable();\r
673         if(timer_pending(&timer_swtd))\r
674                 del_timer(&timer_swtd);\r
675         if ( swtduserenabled ) {\r
676                 swtduserenabled = 0;\r
677                 opencounts = 0;\r
678         }\r
679         misc_deregister(&wdt_miscdev);\r
681         /* Free IRQ */\r
682         free_irq(IRQ_WATCHDOG, NULL);\r
684         /* Free panic hander */\r
685         notifier_chain_unregister(&panic_notifier_list, &swtd_panic_notifier);\r
689 module_init(moxaswtd_init);\r
690 module_exit(moxaswtd_exit);\r
692 MODULE_AUTHOR("Jared_Wu@moxa.com.tw");\r
693 MODULE_LICENSE("GPL");\r
694 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)\r
695 module_param(nowayout, int, 0);\r
696 #else\r
697 MODULE_PARM(nowayout, "i");\r
698 #endif\r
699 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");\r
700 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)\r
701 module_param(debug, int, 0);\r
702 #else\r
703 MODULE_PARM(debug, "i");\r
704 #endif\r
705 MODULE_PARM_DESC(debug, "print the debug message in this driver");\r