watchdog driver for RB 532
[15.05/openwrt.git] / target / linux / rb532 / files / drivers / char / watchdog / rc32434_wdt.c
1 /*
2  *      RC32434_WDT 0.01: IDT Interprise 79RC32434 watchdog driver 
3  *      Copyright (c) Ondrej Zajicek <santiago@crfreenet.org>, 2006
4  *
5  *      based on
6  *
7  *      SoftDog 0.05:   A Software Watchdog Device
8  *
9  *      (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
10  *
11  *      This program is free software; you can redistribute it and/or
12  *      modify it under the terms of the GNU General Public License
13  *      as published by the Free Software Foundation; either version
14  *      2 of the License, or (at your option) any later version.
15  *      
16  */
17
18 #include <linux/module.h>
19 #include <linux/types.h>
20 #include <linux/kernel.h>
21 #include <linux/fs.h>
22 #include <linux/mm.h>
23 #include <linux/miscdevice.h>
24 #include <linux/watchdog.h>
25 #include <linux/reboot.h>
26 #include <linux/smp_lock.h>
27 #include <linux/init.h>
28 #include <asm/bootinfo.h>
29 #include <asm/time.h>
30 #include <asm/uaccess.h>
31 #include <asm/rc32434/integ.h>
32
33 #define DEFAULT_TIMEOUT 15              /* (secs) Default is 15 seconds */
34 #define MAX_TIMEOUT     20              
35 /*
36  * (secs) Max is 20 seconds 
37  * (max frequency of counter is ~200 MHz, counter is 32-bit unsigned int) 
38  */
39
40 #define NAME "rc32434_wdt"
41 #define VERSION "0.1"
42
43 static INTEG_t rc_wdt = (INTEG_t) INTEG_VirtualAddress;                                                                     
44
45 static int expect_close = 0;
46 static int access = 0;
47 static int timeout = 0;
48
49 static int nowayout = WATCHDOG_NOWAYOUT;
50 module_param(nowayout, int, 0);
51 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
52 MODULE_LICENSE("GPL");
53
54
55 static inline void start_wdt(void)
56 {
57         rc_wdt -> wtcount = 0;
58         rc_wdt -> errcs |= ERRCS_wre_m;
59         rc_wdt -> wtc   |= WTC_en_m;
60 }
61
62 static inline void stop_wdt(void)
63 {
64         rc_wdt -> wtc   &= ~WTC_en_m;
65         rc_wdt -> errcs &= ~ERRCS_wre_m;
66 }
67
68 static inline void set_wdt(int new_timeout)
69 {
70         u32 cmp = new_timeout * mips_hpt_frequency;
71         u32 state;
72         
73         timeout = new_timeout;
74         /*
75          * store and disable WTC
76          */
77         state = rc_wdt -> wtc & WTC_en_m;
78         rc_wdt -> wtc   &= ~WTC_en_m;
79
80         rc_wdt -> wtcount = 0;
81         rc_wdt -> wtcompare = cmp;
82
83         /*
84          * restore WTC
85          */     
86         rc_wdt -> wtc |= state;
87 }
88
89 static inline void update_wdt(void)
90 {
91         rc_wdt -> wtcount = 0;
92 }
93
94 /*
95  *      Allow only one person to hold it open
96  */
97  
98 static int wdt_open(struct inode *inode, struct file *file)
99 {
100         if (access)
101                 return -EBUSY;
102         if (nowayout) {
103                 __module_get(THIS_MODULE);
104         }
105         /*
106          *      Activate timer
107          */
108         start_wdt();
109         printk(KERN_INFO NAME ": enabling watchdog timer\n");
110         access = 1;
111         return 0;
112 }
113
114 static int wdt_release(struct inode *inode, struct file *file)
115 {
116         /*
117          *      Shut off the timer.
118          *      Lock it in if it's a module and we set nowayout
119          */
120         if (expect_close && nowayout == 0) {
121                 stop_wdt ();
122                 printk(KERN_INFO NAME ": disabling watchdog timer\n");
123                 module_put(THIS_MODULE);
124         } else {
125                 printk (KERN_CRIT NAME ": device closed unexpectedly.  WDT will not stop!\n");
126         }
127         access = 0;
128         return 0;
129 }
130
131 static ssize_t wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
132 {
133         /*
134          *      Refresh the timer.
135          */
136         if (len) {
137                 if (!nowayout) {
138                         size_t i;
139
140                         /* In case it was set long ago */
141                         expect_close = 0;
142
143                         for (i = 0; i != len; i++) {
144                                 char c;
145                                 if (get_user(c, data + i))
146                                         return -EFAULT;
147                                 if (c == 'V')
148                                         expect_close = 1;
149                         }
150                 }
151                 update_wdt ();
152                 return len;
153         }
154         return 0;
155 }
156
157 static int wdt_ioctl(struct inode *inode, struct file *file,
158         unsigned int cmd, unsigned long arg)
159 {
160         int new_timeout;
161         static struct watchdog_info ident = {
162                 .options =              WDIOF_SETTIMEOUT |
163                                         WDIOF_KEEPALIVEPING |
164                                         WDIOF_MAGICCLOSE,
165                 .firmware_version =     0,
166                 .identity =             "RC32434_WDT Watchdog",
167         };
168         switch (cmd) {
169                 default:
170                         return -ENOTTY;
171                 case WDIOC_GETSUPPORT:
172                         if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
173                                 return -EFAULT;
174                         return 0;
175                 case WDIOC_GETSTATUS:
176                 case WDIOC_GETBOOTSTATUS:
177                         return put_user(0,(int *)arg);
178                 case WDIOC_KEEPALIVE:
179                         update_wdt();
180                         return 0;
181                 case WDIOC_SETTIMEOUT:
182                         if (get_user(new_timeout, (int *)arg))
183                                 return -EFAULT;
184                         if (new_timeout < 1)
185                                 return -EINVAL;
186                         if (new_timeout > MAX_TIMEOUT)
187                                 return -EINVAL;
188                         set_wdt(new_timeout);
189                         /* Fall */
190                 case WDIOC_GETTIMEOUT:
191                         return put_user(timeout, (int *)arg);
192         }
193 }
194
195 static struct file_operations wdt_fops = {
196         owner:          THIS_MODULE,
197         llseek:         no_llseek,
198         write:          wdt_write,
199         ioctl:          wdt_ioctl,
200         open:           wdt_open,
201         release:        wdt_release,
202 };
203
204 static struct miscdevice wdt_miscdev = {
205         minor:          WATCHDOG_MINOR,
206         name:           "watchdog",
207         fops:           &wdt_fops,
208 };
209
210 static char banner[] __initdata = KERN_INFO NAME ": Watchdog Timer version " VERSION ", timer margin: %d sec\n";
211
212 static int __init watchdog_init(void)
213 {
214         int ret;
215
216         /*
217          * There should be check for RC32434 SoC
218          */
219         if (mips_machgroup != MACH_GROUP_MIKROTIK) return -1;
220
221         ret = misc_register(&wdt_miscdev);
222
223         if (ret)
224                 return ret;
225
226         stop_wdt();
227         set_wdt(DEFAULT_TIMEOUT);
228
229         printk(banner, timeout);
230
231         return 0;
232 }
233
234 static void __exit watchdog_exit(void)
235 {
236         misc_deregister(&wdt_miscdev);
237 }
238
239 module_init(watchdog_init);
240 module_exit(watchdog_exit);