PageRenderTime 57ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/release/src/linux/linux/drivers/char/sbc60xxwdt.c

https://gitlab.com/envieidoc/advancedtomato2
C | 346 lines | 190 code | 46 blank | 110 comment | 20 complexity | 4c7f6748cfdc4c7cfe702de6c7db166f MD5 | raw file
  1. /*
  2. * 60xx Single Board Computer Watchdog Timer driver for Linux 2.2.x
  3. *
  4. * Based on acquirewdt.c by Alan Cox.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version
  9. * 2 of the License, or (at your option) any later version.
  10. *
  11. * The author does NOT admit liability nor provide warranty for
  12. * any of this software. This material is provided "AS-IS" in
  13. * the hope that it may be useful for others.
  14. *
  15. * (c) Copyright 2000 Jakob Oestergaard <jakob@ostenfeld.dk>
  16. *
  17. * 12/4 - 2000 [Initial revision]
  18. * 25/4 - 2000 Added /dev/watchdog support
  19. * 09/5 - 2001 [smj@oro.net] fixed fop_write to "return 1" on success
  20. *
  21. *
  22. * Theory of operation:
  23. * A Watchdog Timer (WDT) is a hardware circuit that can
  24. * reset the computer system in case of a software fault.
  25. * You probably knew that already.
  26. *
  27. * Usually a userspace daemon will notify the kernel WDT driver
  28. * via the /proc/watchdog special device file that userspace is
  29. * still alive, at regular intervals. When such a notification
  30. * occurs, the driver will usually tell the hardware watchdog
  31. * that everything is in order, and that the watchdog should wait
  32. * for yet another little while to reset the system.
  33. * If userspace fails (RAM error, kernel bug, whatever), the
  34. * notifications cease to occur, and the hardware watchdog will
  35. * reset the system (causing a reboot) after the timeout occurs.
  36. *
  37. * This WDT driver is different from the other Linux WDT
  38. * drivers in several ways:
  39. * *) The driver will ping the watchdog by itself, because this
  40. * particular WDT has a very short timeout (one second) and it
  41. * would be insane to count on any userspace daemon always
  42. * getting scheduled within that time frame.
  43. * *) This driver expects the userspace daemon to send a specific
  44. * character code ('V') to /dev/watchdog before closing the
  45. * /dev/watchdog file. If the userspace daemon closes the file
  46. * without sending this special character, the driver will assume
  47. * that the daemon (and userspace in general) died, and will
  48. * stop pinging the WDT without disabling it first. This will
  49. * cause a reboot.
  50. *
  51. * Why `V' ? Well, `V' is the character in ASCII for the value 86,
  52. * and we all know that 86 is _the_ most random number in the universe.
  53. * Therefore it is the letter that has the slightest chance of occuring
  54. * by chance, when the system becomes corrupted.
  55. *
  56. */
  57. #include <linux/module.h>
  58. #include <linux/version.h>
  59. #include <linux/types.h>
  60. #include <linux/errno.h>
  61. #include <linux/kernel.h>
  62. #include <linux/timer.h>
  63. #include <linux/sched.h>
  64. #include <linux/miscdevice.h>
  65. #include <linux/watchdog.h>
  66. #include <linux/slab.h>
  67. #include <linux/ioport.h>
  68. #include <linux/fcntl.h>
  69. #include <linux/smp_lock.h>
  70. #include <asm/io.h>
  71. #include <asm/uaccess.h>
  72. #include <asm/system.h>
  73. #include <linux/notifier.h>
  74. #include <linux/reboot.h>
  75. #include <linux/init.h>
  76. #define OUR_NAME "sbc60xxwdt"
  77. /*
  78. * You must set these - The driver cannot probe for the settings
  79. */
  80. #define WDT_STOP 0x45
  81. #define WDT_START 0x443
  82. /*
  83. * The 60xx board can use watchdog timeout values from one second
  84. * to several minutes. The default is one second, so if we reset
  85. * the watchdog every ~250ms we should be safe.
  86. */
  87. #define WDT_INTERVAL (HZ/4+1)
  88. /*
  89. * We must not require too good response from the userspace daemon.
  90. * Here we require the userspace daemon to send us a heartbeat
  91. * char to /dev/watchdog every 10 seconds.
  92. * If the daemon pulses us every 5 seconds, we can still afford
  93. * a 5 second scheduling delay on the (high priority) daemon. That
  94. * should be sufficient for a box under any load.
  95. */
  96. #define WDT_HEARTBEAT (HZ * 10)
  97. static void wdt_timer_ping(unsigned long);
  98. static struct timer_list timer;
  99. static unsigned long next_heartbeat;
  100. static int wdt_is_open;
  101. static int wdt_expect_close;
  102. /*
  103. * Whack the dog
  104. */
  105. static void wdt_timer_ping(unsigned long data)
  106. {
  107. /* If we got a heartbeat pulse within the WDT_US_INTERVAL
  108. * we agree to ping the WDT
  109. */
  110. if(time_before(jiffies, next_heartbeat))
  111. {
  112. /* Ping the WDT by reading from WDT_START */
  113. inb_p(WDT_START);
  114. /* Re-set the timer interval */
  115. timer.expires = jiffies + WDT_INTERVAL;
  116. add_timer(&timer);
  117. } else {
  118. printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n");
  119. }
  120. }
  121. /*
  122. * Utility routines
  123. */
  124. static void wdt_startup(void)
  125. {
  126. next_heartbeat = jiffies + WDT_HEARTBEAT;
  127. /* Start the timer */
  128. timer.expires = jiffies + WDT_INTERVAL;
  129. add_timer(&timer);
  130. printk(OUR_NAME ": Watchdog timer is now enabled.\n");
  131. }
  132. static void wdt_turnoff(void)
  133. {
  134. /* Stop the timer */
  135. del_timer(&timer);
  136. inb_p(WDT_STOP);
  137. printk(OUR_NAME ": Watchdog timer is now disabled...\n");
  138. }
  139. /*
  140. * /dev/watchdog handling
  141. */
  142. static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
  143. {
  144. /* We can't seek */
  145. if(ppos != &file->f_pos)
  146. return -ESPIPE;
  147. /* See if we got the magic character */
  148. if(count)
  149. {
  150. size_t ofs;
  151. /* note: just in case someone wrote the magic character
  152. * five months ago... */
  153. wdt_expect_close = 0;
  154. /* now scan */
  155. for(ofs = 0; ofs != count; ofs++)
  156. {
  157. char c;
  158. if(get_user(c, buf+ofs))
  159. return -EFAULT;
  160. if(c == 'V')
  161. wdt_expect_close = 1;
  162. }
  163. /* Well, anyhow someone wrote to us, we should return that favour */
  164. next_heartbeat = jiffies + WDT_HEARTBEAT;
  165. return 1;
  166. }
  167. return 0;
  168. }
  169. static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos)
  170. {
  171. /* No can do */
  172. return -EINVAL;
  173. }
  174. static int fop_open(struct inode * inode, struct file * file)
  175. {
  176. switch(MINOR(inode->i_rdev))
  177. {
  178. case WATCHDOG_MINOR:
  179. /* Just in case we're already talking to someone... */
  180. if(wdt_is_open)
  181. return -EBUSY;
  182. /* Good, fire up the show */
  183. wdt_is_open = 1;
  184. wdt_startup();
  185. return 0;
  186. default:
  187. return -ENODEV;
  188. }
  189. }
  190. static int fop_close(struct inode * inode, struct file * file)
  191. {
  192. lock_kernel();
  193. if(MINOR(inode->i_rdev) == WATCHDOG_MINOR)
  194. {
  195. if(wdt_expect_close)
  196. wdt_turnoff();
  197. else {
  198. del_timer(&timer);
  199. printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n");
  200. }
  201. }
  202. wdt_is_open = 0;
  203. unlock_kernel();
  204. return 0;
  205. }
  206. static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  207. unsigned long arg)
  208. {
  209. static struct watchdog_info ident=
  210. {
  211. WDIOF_MAGICCLOSE,
  212. 1,
  213. "SB60xx"
  214. };
  215. switch(cmd)
  216. {
  217. default:
  218. return -ENOTTY;
  219. case WDIOC_GETSUPPORT:
  220. return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
  221. case WDIOC_KEEPALIVE:
  222. next_heartbeat = jiffies + WDT_HEARTBEAT;
  223. return 0;
  224. }
  225. }
  226. static struct file_operations wdt_fops = {
  227. owner: THIS_MODULE,
  228. llseek: no_llseek,
  229. read: fop_read,
  230. write: fop_write,
  231. open: fop_open,
  232. release: fop_close,
  233. ioctl: fop_ioctl
  234. };
  235. static struct miscdevice wdt_miscdev = {
  236. WATCHDOG_MINOR,
  237. "watchdog",
  238. &wdt_fops
  239. };
  240. /*
  241. * Notifier for system down
  242. */
  243. static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
  244. void *unused)
  245. {
  246. if(code==SYS_DOWN || code==SYS_HALT)
  247. wdt_turnoff();
  248. return NOTIFY_DONE;
  249. }
  250. /*
  251. * The WDT needs to learn about soft shutdowns in order to
  252. * turn the timebomb registers off.
  253. */
  254. static struct notifier_block wdt_notifier=
  255. {
  256. wdt_notify_sys,
  257. 0,
  258. 0
  259. };
  260. static void __exit sbc60xxwdt_unload(void)
  261. {
  262. wdt_turnoff();
  263. /* Deregister */
  264. misc_deregister(&wdt_miscdev);
  265. unregister_reboot_notifier(&wdt_notifier);
  266. release_region(WDT_START,1);
  267. // release_region(WDT_STOP,1);
  268. }
  269. static int __init sbc60xxwdt_init(void)
  270. {
  271. int rc = -EBUSY;
  272. // We cannot reserve 0x45 - the kernel already has!
  273. // if (!request_region(WDT_STOP, 1, "SBC 60XX WDT"))
  274. // goto err_out;
  275. if (!request_region(WDT_START, 1, "SBC 60XX WDT"))
  276. goto err_out_region1;
  277. init_timer(&timer);
  278. timer.function = wdt_timer_ping;
  279. timer.data = 0;
  280. rc = misc_register(&wdt_miscdev);
  281. if (rc)
  282. goto err_out_region2;
  283. rc = register_reboot_notifier(&wdt_notifier);
  284. if (rc)
  285. goto err_out_miscdev;
  286. printk(KERN_INFO OUR_NAME ": WDT driver for 60XX single board computer initialised.\n");
  287. return 0;
  288. err_out_miscdev:
  289. misc_deregister(&wdt_miscdev);
  290. err_out_region2:
  291. release_region(WDT_START,1);
  292. err_out_region1:
  293. release_region(WDT_STOP,1);
  294. /* err_out: */
  295. return rc;
  296. }
  297. module_init(sbc60xxwdt_init);
  298. module_exit(sbc60xxwdt_unload);
  299. MODULE_LICENSE("GPL");
  300. EXPORT_NO_SYMBOLS;