~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux-2.6.17/drivers/char/watchdog/shwdt.c

Version: ~ [ 2.6.16 ] ~ [ 2.6.17 ] ~
Architecture: ~ [ ia64 ] ~ [ i386 ] ~ [ arm ] ~ [ ppc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.