ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / char / watchdog / shwdt.c
1 /*
2  * drivers/char/watchdog/shwdt.c
3  *
4  * Watchdog driver for integrated watchdog in the SuperH processors.
5  *
6  * Copyright (C) 2001, 2002, 2003 Paul Mundt <lethal@linux-sh.org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2 of the License, or (at your
11  * option) any later version.
12  *
13  * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
14  *     Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
15  *
16  * 19-Apr-2002 Rob Radez <rob@osinvestor.com>
17  *     Added expect close support, made emulated timeout runtime changeable
18  *     general cleanups, add some ioctls
19  */
20 #include <linux/config.h>
21 #include <linux/module.h>
22 #include <linux/moduleparam.h>
23 #include <linux/init.h>
24 #include <linux/types.h>
25 #include <linux/miscdevice.h>
26 #include <linux/watchdog.h>
27 #include <linux/reboot.h>
28 #include <linux/notifier.h>
29 #include <linux/ioport.h>
30 #include <linux/fs.h>
31
32 #include <asm/io.h>
33 #include <asm/uaccess.h>
34 #include <asm/watchdog.h>
35
36 #define PFX "shwdt: "
37
38 /*
39  * Default clock division ratio is 5.25 msecs. For an additional table of
40  * values, consult the asm-sh/watchdog.h. Overload this at module load
41  * time.
42  *
43  * In order for this to work reliably we need to have HZ set to 1000 or
44  * something quite higher than 100 (or we need a proper high-res timer
45  * implementation that will deal with this properly), otherwise the 10ms
46  * resolution of a jiffy is enough to trigger the overflow. For things like
47  * the SH-4 and SH-5, this isn't necessarily that big of a problem, though
48  * for the SH-2 and SH-3, this isn't recommended unless the WDT is absolutely
49  * necssary.
50  *
51  * As a result of this timing problem, the only modes that are particularly
52  * feasible are the 4096 and the 2048 divisors, which yeild 5.25 and 2.62ms
53  * overflow periods respectively.
54  *
55  * Also, since we can't really expect userspace to be responsive enough
56  * before the overflow happens, we maintain two seperate timers .. One in
57  * the kernel for clearing out WOVF every 2ms or so (again, this depends on
58  * HZ == 1000), and another for monitoring userspace writes to the WDT device.
59  *
60  * As such, we currently use a configurable heartbeat interval which defaults
61  * to 30s. In this case, the userspace daemon is only responsible for periodic
62  * writes to the device before the next heartbeat is scheduled. If the daemon
63  * misses its deadline, the kernel timer will allow the WDT to overflow.
64  */
65 static int clock_division_ratio = WTCSR_CKS_4096;
66
67 #define msecs_to_jiffies(msecs) (jiffies + (HZ * msecs + 9999) / 10000)
68 #define next_ping_period(cks)   msecs_to_jiffies(cks - 4)
69
70 static unsigned long shwdt_is_open;
71 static struct watchdog_info sh_wdt_info;
72 static char shwdt_expect_close;
73 static struct timer_list timer;
74 static unsigned long next_heartbeat;
75
76 #define WATCHDOG_HEARTBEAT 30                   /* 30 sec default heartbeat */
77 static int heartbeat = WATCHDOG_HEARTBEAT;      /* in seconds */
78
79 #ifdef CONFIG_WATCHDOG_NOWAYOUT
80 static int nowayout = 1;
81 #else
82 static int nowayout = 0;
83 #endif
84
85 /**
86  *      sh_wdt_start - Start the Watchdog
87  *
88  *      Starts the watchdog.
89  */
90 static void sh_wdt_start(void)
91 {
92         __u8 csr;
93
94         next_heartbeat = jiffies + (heartbeat * HZ);
95         mod_timer(&timer, next_ping_period(clock_division_ratio));
96
97         csr = sh_wdt_read_csr();
98         csr |= WTCSR_WT | clock_division_ratio;
99         sh_wdt_write_csr(csr);
100
101         sh_wdt_write_cnt(0);
102
103         /*
104          * These processors have a bit of an inconsistent initialization
105          * process.. starting with SH-3, RSTS was moved to WTCSR, and the
106          * RSTCSR register was removed.
107          *
108          * On the SH-2 however, in addition with bits being in different
109          * locations, we must deal with RSTCSR outright..
110          */
111         csr = sh_wdt_read_csr();
112         csr |= WTCSR_TME;
113         csr &= ~WTCSR_RSTS;
114         sh_wdt_write_csr(csr);
115
116 #ifdef CONFIG_CPU_SH2
117         /*
118          * Whoever came up with the RSTCSR semantics must've been smoking
119          * some of the good stuff, since in addition to the WTCSR/WTCNT write
120          * brain-damage, it's managed to fuck things up one step further..
121          *
122          * If we need to clear the WOVF bit, the upper byte has to be 0xa5..
123          * but if we want to touch RSTE or RSTS, the upper byte has to be
124          * 0x5a..
125          */
126         csr = sh_wdt_read_rstcsr();
127         csr &= ~RSTCSR_RSTS;
128         sh_wdt_write_rstcsr(csr);
129 #endif
130 }
131
132 /**
133  *      sh_wdt_stop - Stop the Watchdog
134  *
135  *      Stops the watchdog.
136  */
137 static void sh_wdt_stop(void)
138 {
139         __u8 csr;
140
141         del_timer(&timer);
142
143         csr = sh_wdt_read_csr();
144         csr &= ~WTCSR_TME;
145         sh_wdt_write_csr(csr);
146 }
147
148 /**
149  *      sh_wdt_keepalive - Keep the Userspace Watchdog Alive
150  *
151  *      The Userspace watchdog got a KeepAlive: schedule the next heartbeat.
152  */
153 static void sh_wdt_keepalive(void)
154 {
155         next_heartbeat = jiffies + (heartbeat * HZ);
156 }
157
158 /**
159  *      sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat
160  *
161  *      Set the Userspace Watchdog heartbeat
162  */
163 static int sh_wdt_set_heartbeat(int t)
164 {
165         if ((t < 1) || (t > 3600)) /* arbitrary upper limit */
166                 return -EINVAL;
167
168         heartbeat = t;
169         return 0;
170 }
171
172 /**
173  *      sh_wdt_ping - Ping the Watchdog
174  *
175  *      @data: Unused
176  *
177  *      Clears overflow bit, resets timer counter.
178  */
179 static void sh_wdt_ping(unsigned long data)
180 {
181         if (time_before(jiffies, next_heartbeat)) {
182                 __u8 csr;
183
184                 csr = sh_wdt_read_csr();
185                 csr &= ~WTCSR_IOVF;
186                 sh_wdt_write_csr(csr);
187
188                 sh_wdt_write_cnt(0);
189
190                 mod_timer(&timer, next_ping_period(clock_division_ratio));
191         } else {
192                 printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n");
193         }
194 }
195
196 /**
197  *      sh_wdt_open - Open the Device
198  *
199  *      @inode: inode of device
200  *      @file: file handle of device
201  *
202  *      Watchdog device is opened and started.
203  */
204 static int sh_wdt_open(struct inode *inode, struct file *file)
205 {
206         if (test_and_set_bit(0, &shwdt_is_open))
207                 return -EBUSY;
208         if (nowayout)
209                 __module_get(THIS_MODULE);
210
211         sh_wdt_start();
212
213         return 0;
214 }
215
216 /**
217  *      sh_wdt_close - Close the Device
218  *
219  *      @inode: inode of device
220  *      @file: file handle of device
221  *
222  *      Watchdog device is closed and stopped.
223  */
224 static int sh_wdt_close(struct inode *inode, struct file *file)
225 {
226         if (shwdt_expect_close == 42) {
227                 sh_wdt_stop();
228         } else {
229                 printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
230                 sh_wdt_keepalive();
231         }
232
233         clear_bit(0, &shwdt_is_open);
234         shwdt_expect_close = 0;
235
236         return 0;
237 }
238
239 /**
240  *      sh_wdt_write - Write to Device
241  *
242  *      @file: file handle of device
243  *      @buf: buffer to write
244  *      @count: length of buffer
245  *      @ppos: offset
246  *
247  *      Pings the watchdog on write.
248  */
249 static ssize_t sh_wdt_write(struct file *file, const char *buf,
250                             size_t count, loff_t *ppos)
251 {
252         /* Can't seek (pwrite) on this device */
253         if (ppos != &file->f_pos)
254                 return -ESPIPE;
255
256         if (count) {
257                 if (!nowayout) {
258                         size_t i;
259
260                         shwdt_expect_close = 0;
261
262                         for (i = 0; i != count; i++) {
263                                 char c;
264                                 if (get_user(c, buf + i))
265                                         return -EFAULT;
266                                 if (c == 'V')
267                                         shwdt_expect_close = 42;
268                         }
269                 }
270                 sh_wdt_keepalive();
271         }
272
273         return count;
274 }
275
276 /**
277  *      sh_wdt_ioctl - Query Device
278  *
279  *      @inode: inode of device
280  *      @file: file handle of device
281  *      @cmd: watchdog command
282  *      @arg: argument
283  *
284  *      Query basic information from the device or ping it, as outlined by the
285  *      watchdog API.
286  */
287 static int sh_wdt_ioctl(struct inode *inode, struct file *file,
288                         unsigned int cmd, unsigned long arg)
289 {
290         int new_heartbeat;
291         int options, retval = -EINVAL;
292
293         switch (cmd) {
294                 case WDIOC_GETSUPPORT:
295                         return copy_to_user((struct watchdog_info *)arg,
296                                           &sh_wdt_info,
297                                           sizeof(sh_wdt_info)) ? -EFAULT : 0;
298                 case WDIOC_GETSTATUS:
299                 case WDIOC_GETBOOTSTATUS:
300                         return put_user(0, (int *)arg);
301                 case WDIOC_KEEPALIVE:
302                         sh_wdt_keepalive();
303                         return 0;
304                 case WDIOC_SETTIMEOUT:
305                         if (get_user(new_heartbeat, (int *)arg))
306                                 return -EFAULT;
307
308                         if (sh_wdt_set_heartbeat(new_heartbeat))
309                                 return -EINVAL;
310
311                         sh_wdt_keepalive();
312                         /* Fall */
313                 case WDIOC_GETTIMEOUT:
314                         return put_user(heartbeat, (int *)arg);
315                 case WDIOC_SETOPTIONS:
316                         if (get_user(options, (int *)arg))
317                                 return -EFAULT;
318
319                         if (options & WDIOS_DISABLECARD) {
320                                 sh_wdt_stop();
321                                 retval = 0;
322                         }
323
324                         if (options & WDIOS_ENABLECARD) {
325                                 sh_wdt_start();
326                                 retval = 0;
327                         }
328
329                         return retval;
330                 default:
331                         return -ENOIOCTLCMD;
332         }
333
334         return 0;
335 }
336
337 /**
338  *      sh_wdt_notify_sys - Notifier Handler
339  *
340  *      @this: notifier block
341  *      @code: notifier event
342  *      @unused: unused
343  *
344  *      Handles specific events, such as turning off the watchdog during a
345  *      shutdown event.
346  */
347 static int sh_wdt_notify_sys(struct notifier_block *this,
348                              unsigned long code, void *unused)
349 {
350         if (code == SYS_DOWN || code == SYS_HALT) {
351                 sh_wdt_stop();
352         }
353
354         return NOTIFY_DONE;
355 }
356
357 static struct file_operations sh_wdt_fops = {
358         .owner          = THIS_MODULE,
359         .llseek         = no_llseek,
360         .write          = sh_wdt_write,
361         .ioctl          = sh_wdt_ioctl,
362         .open           = sh_wdt_open,
363         .release        = sh_wdt_close,
364 };
365
366 static struct watchdog_info sh_wdt_info = {
367         .options                = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
368         .firmware_version       = 1,
369         .identity               = "SH WDT",
370 };
371
372 static struct notifier_block sh_wdt_notifier = {
373         .notifier_call          = sh_wdt_notify_sys,
374 };
375
376 static struct miscdevice sh_wdt_miscdev = {
377         .minor          = WATCHDOG_MINOR,
378         .name           = "watchdog",
379         .fops           = &sh_wdt_fops,
380 };
381
382 /**
383  *      sh_wdt_init - Initialize module
384  *
385  *      Registers the device and notifier handler. Actual device
386  *      initialization is handled by sh_wdt_open().
387  */
388 static int __init sh_wdt_init(void)
389 {
390         int rc;
391
392         if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) {
393                 clock_division_ratio = WTCSR_CKS_4096;
394                 printk(KERN_INFO PFX "clock_division_ratio value must be 0x5<=x<=0x7, using %d\n",
395                         clock_division_ratio);
396         }
397
398         if (sh_wdt_set_heartbeat(heartbeat))
399         {
400                 heartbeat = WATCHDOG_HEARTBEAT;
401                 printk(KERN_INFO PFX "heartbeat value must be 1<=x<=3600, using %d\n",
402                         heartbeat);
403         }
404
405         init_timer(&timer);
406         timer.function = sh_wdt_ping;
407         timer.data = 0;
408
409         rc = register_reboot_notifier(&sh_wdt_notifier);
410         if (rc) {
411                 printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", rc);
412                 return rc;
413         }
414
415         rc = misc_register(&sh_wdt_miscdev);
416         if (rc) {
417                 printk(KERN_ERR PFX "Can't register miscdev on minor=%d (err=%d)\n",
418                         sh_wdt_miscdev.minor, rc);
419                 unregister_reboot_notifier(&sh_wdt_notifier);
420                 return rc;
421         }
422
423         printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
424                 heartbeat, nowayout);
425
426         return 0;
427 }
428
429 /**
430  *      sh_wdt_exit - Deinitialize module
431  *
432  *      Unregisters the device and notifier handler. Actual device
433  *      deinitialization is handled by sh_wdt_close().
434  */
435 static void __exit sh_wdt_exit(void)
436 {
437         misc_deregister(&sh_wdt_miscdev);
438         unregister_reboot_notifier(&sh_wdt_notifier);
439 }
440
441 MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
442 MODULE_DESCRIPTION("SuperH watchdog driver");
443 MODULE_LICENSE("GPL");
444 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
445
446 module_param(clock_division_ratio, int, 0);
447 MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7.");
448
449 module_param(heartbeat, int, 0);
450 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
451
452 module_param(nowayout, int, 0);
453 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
454
455 module_init(sh_wdt_init);
456 module_exit(sh_wdt_exit);
457