Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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 #include <linux/mm.h>
 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 separate 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 void sh_wdt_ping(unsigned long data);
 69 
 70 static unsigned long shwdt_is_open;
 71 static struct watchdog_info sh_wdt_info;
 72 static char shwdt_expect_close;
 73 static DEFINE_TIMER(timer, sh_wdt_ping, 0, 0);
 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 static int nowayout = WATCHDOG_NOWAYOUT;
 80 
 81 /**
 82  *      sh_wdt_start - Start the Watchdog
 83  *
 84  *      Starts the watchdog.
 85  */
 86 static void sh_wdt_start(void)
 87 {
 88         __u8 csr;
 89 
 90         next_heartbeat = jiffies + (heartbeat * HZ);
 91         mod_timer(&timer, next_ping_period(clock_division_ratio));
 92 
 93         csr = sh_wdt_read_csr();
 94         csr |= WTCSR_WT | clock_division_ratio;
 95         sh_wdt_write_csr(csr);
 96 
 97         sh_wdt_write_cnt(0);
 98 
 99         /*
100          * These processors have a bit of an inconsistent initialization
101          * process.. starting with SH-3, RSTS was moved to WTCSR, and the
102          * RSTCSR register was removed.
103          *
104          * On the SH-2 however, in addition with bits being in different
105          * locations, we must deal with RSTCSR outright..
106          */
107         csr = sh_wdt_read_csr();
108         csr |= WTCSR_TME;
109         csr &= ~WTCSR_RSTS;
110         sh_wdt_write_csr(csr);
111 
112 #ifdef CONFIG_CPU_SH2
113         /*
114          * Whoever came up with the RSTCSR semantics must've been smoking
115          * some of the good stuff, since in addition to the WTCSR/WTCNT write
116          * brain-damage, it's managed to fuck things up one step further..
117          *
118          * If we need to clear the WOVF bit, the upper byte has to be 0xa5..
119          * but if we want to touch RSTE or RSTS, the upper byte has to be
120          * 0x5a..
121          */
122         csr = sh_wdt_read_rstcsr();
123         csr &= ~RSTCSR_RSTS;
124         sh_wdt_write_rstcsr(csr);
125 #endif
126 }
127 
128 /**
129  *      sh_wdt_stop - Stop the Watchdog
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  *      The Userspace watchdog got a KeepAlive: schedule the next heartbeat.
146  */
147 static inline 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  *      Set the Userspace Watchdog heartbeat
155  */
156 static int sh_wdt_set_heartbeat(int t)
157 {
158         if (unlikely((t < 1) || (t > 3600))) /* arbitrary upper limit */
159                 return -EINVAL;
160 
161         heartbeat = t;
162         return 0;
163 }
164 
165 /**
166  *      sh_wdt_ping - Ping the Watchdog
167  *      @data: Unused
168  *
169  *      Clears overflow bit, resets timer counter.
170  */
171 static void sh_wdt_ping(unsigned long data)
172 {
173         if (time_before(jiffies, next_heartbeat)) {
174                 __u8 csr;
175 
176                 csr = sh_wdt_read_csr();
177                 csr &= ~WTCSR_IOVF;
178                 sh_wdt_write_csr(csr);
179 
180                 sh_wdt_write_cnt(0);
181 
182                 mod_timer(&timer, next_ping_period(clock_division_ratio));
183         } else
184                 printk(KERN_WARNING PFX "Heartbeat lost! Will not ping "
185                        "the watchdog\n");
186 }
187 
188 /**
189  *      sh_wdt_open - Open the Device
190  *      @inode: inode of device
191  *      @file: file handle of device
192  *
193  *      Watchdog device is opened and started.
194  */
195 static int sh_wdt_open(struct inode *inode, struct file *file)
196 {
197         if (test_and_set_bit(0, &shwdt_is_open))
198                 return -EBUSY;
199         if (nowayout)
200                 __module_get(THIS_MODULE);
201 
202         sh_wdt_start();
203 
204         return nonseekable_open(inode, file);
205 }
206 
207 /**
208  *      sh_wdt_close - Close the Device
209  *      @inode: inode of device
210  *      @file: file handle of device
211  *
212  *      Watchdog device is closed and stopped.
213  */
214 static int sh_wdt_close(struct inode *inode, struct file *file)
215 {
216         if (shwdt_expect_close == 42) {
217                 sh_wdt_stop();
218         } else {
219                 printk(KERN_CRIT PFX "Unexpected close, not "
220                        "stopping watchdog!\n");
221                 sh_wdt_keepalive();
222         }
223 
224         clear_bit(0, &shwdt_is_open);
225         shwdt_expect_close = 0;
226 
227         return 0;
228 }
229 
230 /**
231  *      sh_wdt_write - Write to Device
232  *      @file: file handle of device
233  *      @buf: buffer to write
234  *      @count: length of buffer
235  *      @ppos: offset
236  *
237  *      Pings the watchdog on write.
238  */
239 static ssize_t sh_wdt_write(struct file *file, const char *buf,
240                             size_t count, loff_t *ppos)
241 {
242         if (count) {
243                 if (!nowayout) {
244                         size_t i;
245 
246                         shwdt_expect_close = 0;
247 
248                         for (i = 0; i != count; i++) {
249                                 char c;
250                                 if (get_user(c, buf + i))
251                                         return -EFAULT;
252                                 if (c == 'V')
253                                         shwdt_expect_close = 42;
254                         }
255                 }
256                 sh_wdt_keepalive();
257         }
258 
259         return count;
260 }
261 
262 /**
263  *      sh_wdt_mmap - map WDT/CPG registers into userspace
264  *      @file: file structure for the device
265  *      @vma: VMA to map the registers into
266  *
267  *      A simple mmap() implementation for the corner cases where the counter
268  *      needs to be mapped in userspace directly. Due to the relatively small
269  *      size of the area, neighbouring registers not necessarily tied to the
270  *      CPG will also be accessible through the register page, so this remains
271  *      configurable for users that really know what they're doing.
272  *
273  *      Additionaly, the register page maps in the CPG register base relative
274  *      to the nearest page-aligned boundary, which requires that userspace do
275  *      the appropriate CPU subtype math for calculating the page offset for
276  *      the counter value.
277  */
278 static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma)
279 {
280         int ret = -ENOSYS;
281 
282 #ifdef CONFIG_SH_WDT_MMAP
283         unsigned long addr;
284 
285         /* Only support the simple cases where we map in a register page. */
286         if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff)
287                 return -EINVAL;
288 
289         /*
290          * Pick WTCNT as the start, it's usually the first register after the
291          * FRQCR, and neither one are generally page-aligned out of the box.
292          */
293         addr = WTCNT & ~(PAGE_SIZE - 1);
294 
295         vma->vm_flags |= VM_IO;
296         vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
297 
298         if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
299                                PAGE_SIZE, vma->vm_page_prot)) {
300                 printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n",
301                        __func__);
302                 return -EAGAIN;
303         }
304 
305         ret = 0;
306 #endif
307 
308         return ret;
309 }
310 
311 /**
312  *      sh_wdt_ioctl - Query Device
313  *      @inode: inode of device
314  *      @file: file handle of device
315  *      @cmd: watchdog command
316  *      @arg: argument
317  *
318  *      Query basic information from the device or ping it, as outlined by the
319  *      watchdog API.
320  */
321 static int sh_wdt_ioctl(struct inode *inode, struct file *file,
322                         unsigned int cmd, unsigned long arg)
323 {
324         int new_heartbeat;
325         int options, retval = -EINVAL;
326 
327         switch (cmd) {
328                 case WDIOC_GETSUPPORT:
329                         return copy_to_user((struct watchdog_info *)arg,
330                                           &sh_wdt_info,
331                                           sizeof(sh_wdt_info)) ? -EFAULT : 0;
332                 case WDIOC_GETSTATUS:
333                 case WDIOC_GETBOOTSTATUS:
334                         return put_user(0, (int *)arg);
335                 case WDIOC_KEEPALIVE:
336                         sh_wdt_keepalive();
337                         return 0;
338                 case WDIOC_SETTIMEOUT:
339                         if (get_user(new_heartbeat, (int *)arg))
340                                 return -EFAULT;
341 
342                         if (sh_wdt_set_heartbeat(new_heartbeat))
343                                 return -EINVAL;
344 
345                         sh_wdt_keepalive();
346                         /* Fall */
347                 case WDIOC_GETTIMEOUT:
348                         return put_user(heartbeat, (int *)arg);
349                 case WDIOC_SETOPTIONS:
350                         if (get_user(options, (int *)arg))
351                                 return -EFAULT;
352 
353                         if (options & WDIOS_DISABLECARD) {
354                                 sh_wdt_stop();
355                                 retval = 0;
356                         }
357 
358                         if (options & WDIOS_ENABLECARD) {
359                                 sh_wdt_start();
360                                 retval = 0;
361                         }
362 
363                         return retval;
364                 default:
365                         return -ENOTTY;
366         }
367 
368         return 0;
369 }
370 
371 /**
372  *      sh_wdt_notify_sys - Notifier Handler
373  *      @this: notifier block
374  *      @code: notifier event
375  *      @unused: unused
376  *
377  *      Handles specific events, such as turning off the watchdog during a
378  *      shutdown event.
379  */
380 static int sh_wdt_notify_sys(struct notifier_block *this,
381                              unsigned long code, void *unused)
382 {
383         if (code == SYS_DOWN || code == SYS_HALT)
384                 sh_wdt_stop();
385 
386         return NOTIFY_DONE;
387 }
388 
389 static const struct file_operations sh_wdt_fops = {
390         .owner          = THIS_MODULE,
391         .llseek         = no_llseek,
392         .write          = sh_wdt_write,
393         .ioctl          = sh_wdt_ioctl,
394         .open           = sh_wdt_open,
395         .release        = sh_wdt_close,
396         .mmap           = sh_wdt_mmap,
397 };
398 
399 static struct watchdog_info sh_wdt_info = {
400         .options                = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
401                                   WDIOF_MAGICCLOSE,
402         .firmware_version       = 1,
403         .identity               = "SH WDT",
404 };
405 
406 static struct notifier_block sh_wdt_notifier = {
407         .notifier_call          = sh_wdt_notify_sys,
408 };
409 
410 static struct miscdevice sh_wdt_miscdev = {
411         .minor          = WATCHDOG_MINOR,
412         .name           = "watchdog",
413         .fops           = &sh_wdt_fops,
414 };
415 
416 /**
417  *      sh_wdt_init - Initialize module
418  *      Registers the device and notifier handler. Actual device
419  *      initialization is handled by sh_wdt_open().
420  */
421 static int __init sh_wdt_init(void)
422 {
423         int rc;
424 
425         if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) {
426                 clock_division_ratio = WTCSR_CKS_4096;
427                 printk(KERN_INFO PFX "clock_division_ratio value must "
428                        "be 0x5<=x<=0x7, using %d\n", clock_division_ratio);
429         }
430 
431         rc = sh_wdt_set_heartbeat(heartbeat);
432         if (unlikely(rc)) {
433                 heartbeat = WATCHDOG_HEARTBEAT;
434                 printk(KERN_INFO PFX "heartbeat value must "
435                        "be 1<=x<=3600, using %d\n", heartbeat);
436         }
437 
438         rc = register_reboot_notifier(&sh_wdt_notifier);
439         if (unlikely(rc)) {
440                 printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n",
441                        rc);
442                 return rc;
443         }
444 
445         rc = misc_register(&sh_wdt_miscdev);
446         if (unlikely(rc)) {
447                 printk(KERN_ERR PFX "Can't register miscdev on "
448                        "minor=%d (err=%d)\n", sh_wdt_miscdev.minor, rc);
449                 unregister_reboot_notifier(&sh_wdt_notifier);
450                 return rc;
451         }
452 
453         printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
454                 heartbeat, nowayout);
455 
456         return 0;
457 }
458 
459 /**
460  *      sh_wdt_exit - Deinitialize module
461  *      Unregisters the device and notifier handler. Actual device
462  *      deinitialization is handled by sh_wdt_close().
463  */
464 static void __exit sh_wdt_exit(void)
465 {
466         misc_deregister(&sh_wdt_miscdev);
467         unregister_reboot_notifier(&sh_wdt_notifier);
468 }
469 
470 MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
471 MODULE_DESCRIPTION("SuperH watchdog driver");
472 MODULE_LICENSE("GPL");
473 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
474 
475 module_param(clock_division_ratio, int, 0);
476 MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). (default=" __MODULE_STRING(clock_division_ratio) ")");
477 
478 module_param(heartbeat, int, 0);
479 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
480 
481 module_param(nowayout, int, 0);
482 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
483 
484 module_init(sh_wdt_init);
485 module_exit(sh_wdt_exit);
486 
  This page was automatically generated by the LXR engine.