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