ib700wdt.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:7k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * IB700 Single Board Computer WDT driver for Linux 2.4.x
  3.  *
  4.  * (c) Copyright 2001 Charles Howes <chowes@vsol.net>
  5.  *
  6.  *      Based on advantechwdt.c which is based on acquirewdt.c which
  7.  *       is based on wdt.c.
  8.  *
  9.  * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
  10.  *
  11.  * Based on acquirewdt.c which is based on wdt.c.
  12.  * Original copyright messages:
  13.  *
  14.  * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
  15.  * http://www.redhat.com
  16.  *
  17.  * This program is free software; you can redistribute it and/or
  18.  * modify it under the terms of the GNU General Public License
  19.  * as published by the Free Software Foundation; either version
  20.  * 2 of the License, or (at your option) any later version.
  21.  *
  22.  * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
  23.  * warranty for any of this software. This material is provided
  24.  * "AS-IS" and at no charge.
  25.  *
  26.  * (c) Copyright 1995    Alan Cox <alan@redhat.com>
  27.  *
  28.  */
  29. #include <linux/config.h>
  30. #include <linux/module.h>
  31. #include <linux/version.h>
  32. #include <linux/types.h>
  33. #include <linux/errno.h>
  34. #include <linux/kernel.h>
  35. #include <linux/sched.h>
  36. #include <linux/miscdevice.h>
  37. #include <linux/watchdog.h>
  38. #include <linux/slab.h>
  39. #include <linux/ioport.h>
  40. #include <linux/fcntl.h>
  41. #include <asm/io.h>
  42. #include <asm/uaccess.h>
  43. #include <asm/system.h>
  44. #include <linux/notifier.h>
  45. #include <linux/reboot.h>
  46. #include <linux/init.h>
  47. #include <linux/spinlock.h>
  48. #include <linux/smp_lock.h>
  49. static int ibwdt_is_open;
  50. static spinlock_t ibwdt_lock;
  51. static int expect_close = 0;
  52. /*
  53.  *
  54.  * Watchdog Timer Configuration
  55.  *
  56.  * The function of the watchdog timer is to reset the system
  57.  * automatically and is defined at I/O port 0443H.  To enable the
  58.  * watchdog timer and allow the system to reset, write I/O port 0443H.
  59.  * To disable the timer, write I/O port 0441H for the system to stop the
  60.  * watchdog function.  The timer has a tolerance of 20% for its
  61.  * intervals.
  62.  *
  63.  * The following describes how the timer should be programmed.
  64.  *
  65.  * Enabling Watchdog:
  66.  * MOV AX,000FH (Choose the values from 0 to F)
  67.  * MOV DX,0443H
  68.  * OUT DX,AX
  69.  *
  70.  * Disabling Watchdog:
  71.  * MOV AX,000FH (Any value is fine.)
  72.  * MOV DX,0441H
  73.  * OUT DX,AX
  74.  *
  75.  * Watchdog timer control table:
  76.  * Level   Value  Time/sec | Level Value Time/sec
  77.  *   1       F       0     |   9     7      16
  78.  *   2       E       2     |   10    6      18
  79.  *   3       D       4     |   11    5      20
  80.  *   4       C       6     |   12    4      22
  81.  *   5       B       8     |   13    3      24
  82.  *   6       A       10    |   14    2      26
  83.  *   7       9       12    |   15    1      28
  84.  *   8       8       14    |   16    0      30
  85.  *
  86.  */
  87. static int wd_times[] = {
  88. 30, /* 0x0 */
  89. 28, /* 0x1 */
  90. 26, /* 0x2 */
  91. 24, /* 0x3 */
  92. 22, /* 0x4 */
  93. 20, /* 0x5 */
  94. 18, /* 0x6 */
  95. 16, /* 0x7 */
  96. 14, /* 0x8 */
  97. 12, /* 0x9 */
  98. 10, /* 0xA */
  99. 8, /* 0xB */
  100. 6, /* 0xC */
  101. 4, /* 0xD */
  102. 2, /* 0xE */
  103. 0, /* 0xF */
  104. };
  105. #define WDT_STOP 0x441
  106. #define WDT_START 0x443
  107. /* Default timeout */
  108. #define WD_TIMO 0 /* 30 seconds +/- 20%, from table */
  109. static int wd_margin = WD_TIMO;
  110. #ifdef CONFIG_WATCHDOG_NOWAYOUT
  111. static int nowayout = 1;
  112. #else
  113. static int nowayout = 0;
  114. #endif
  115. MODULE_PARM(nowayout,"i");
  116. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
  117. /*
  118.  * Kernel methods.
  119.  */
  120. static void
  121. ibwdt_ping(void)
  122. {
  123. /* Write a watchdog value */
  124. outb_p(wd_times[wd_margin], WDT_START);
  125. }
  126. static ssize_t
  127. ibwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
  128. {
  129. /*  Can't seek (pwrite) on this device  */
  130. if (ppos != &file->f_pos)
  131. return -ESPIPE;
  132. if (count) {
  133. if (!nowayout) {
  134. size_t i;
  135. /* In case it was set long ago */
  136. expect_close = 0;
  137. for (i = 0; i != count; i++) {
  138. char c;
  139. if (get_user(c, buf + i))
  140. return -EFAULT;
  141. if (c == 'V')
  142. expect_close = 1;
  143. }
  144. }
  145. ibwdt_ping();
  146. return 1;
  147. }
  148. return 0;
  149. }
  150. static ssize_t
  151. ibwdt_read(struct file *file, char *buf, size_t count, loff_t *ppos)
  152. {
  153. return -EINVAL;
  154. }
  155. static int
  156. ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  157.   unsigned long arg)
  158. {
  159. int i, new_margin;
  160. static struct watchdog_info ident = {
  161. WDIOF_KEEPALIVEPING |
  162. WDIOF_SETTIMEOUT |
  163. WDIOF_MAGICCLOSE,
  164. 1, "IB700 WDT"
  165. };
  166. switch (cmd) {
  167. case WDIOC_GETSUPPORT:
  168.   if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
  169.     return -EFAULT;
  170.   break;
  171. case WDIOC_GETSTATUS:
  172.   if (copy_to_user((int *)arg, &ibwdt_is_open,  sizeof(int)))
  173.     return -EFAULT;
  174.   break;
  175. case WDIOC_KEEPALIVE:
  176.   ibwdt_ping();
  177.   break;
  178. case WDIOC_SETTIMEOUT:
  179.   if (get_user(new_margin, (int *)arg))
  180.   return -EFAULT;
  181.   if ((new_margin < 0) || (new_margin > 30))
  182.   return -EINVAL;
  183.   for (i = 0x0F; i > -1; i--)
  184.   if (wd_times[i] > new_margin)
  185.   break;
  186.   wd_margin = i;
  187.   ibwdt_ping();
  188.   /* Fall */
  189. case WDIOC_GETTIMEOUT:
  190.   return put_user(wd_times[wd_margin], (int *)arg);
  191.   break;
  192. default:
  193.   return -ENOTTY;
  194. }
  195. return 0;
  196. }
  197. static int
  198. ibwdt_open(struct inode *inode, struct file *file)
  199. {
  200. switch (MINOR(inode->i_rdev)) {
  201. case WATCHDOG_MINOR:
  202. spin_lock(&ibwdt_lock);
  203. if (ibwdt_is_open) {
  204. spin_unlock(&ibwdt_lock);
  205. return -EBUSY;
  206. }
  207. /*
  208.  * Activate
  209.  */
  210. ibwdt_is_open = 1;
  211. ibwdt_ping();
  212. spin_unlock(&ibwdt_lock);
  213. return 0;
  214. default:
  215. return -ENODEV;
  216. }
  217. }
  218. static int
  219. ibwdt_close(struct inode *inode, struct file *file)
  220. {
  221. lock_kernel();
  222. if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
  223. spin_lock(&ibwdt_lock);
  224. if (expect_close) {
  225. outb_p(wd_times[wd_margin], WDT_STOP);
  226. } else {
  227. printk(KERN_CRIT "WDT device closed unexpectedly.  WDT will not stop!n");
  228. }
  229. ibwdt_is_open = 0;
  230. spin_unlock(&ibwdt_lock);
  231. }
  232. unlock_kernel();
  233. return 0;
  234. }
  235. /*
  236.  * Notifier for system down
  237.  */
  238. static int
  239. ibwdt_notify_sys(struct notifier_block *this, unsigned long code,
  240. void *unused)
  241. {
  242. if (code == SYS_DOWN || code == SYS_HALT) {
  243. /* Turn the WDT off */
  244. outb_p(wd_times[wd_margin], WDT_STOP);
  245. }
  246. return NOTIFY_DONE;
  247. }
  248. /*
  249.  * Kernel Interfaces
  250.  */
  251. static struct file_operations ibwdt_fops = {
  252. owner: THIS_MODULE,
  253. read: ibwdt_read,
  254. write: ibwdt_write,
  255. ioctl: ibwdt_ioctl,
  256. open: ibwdt_open,
  257. release: ibwdt_close,
  258. };
  259. static struct miscdevice ibwdt_miscdev = {
  260. WATCHDOG_MINOR,
  261. "watchdog",
  262. &ibwdt_fops
  263. };
  264. /*
  265.  * The WDT needs to learn about soft shutdowns in order to
  266.  * turn the timebomb registers off.
  267.  */
  268. static struct notifier_block ibwdt_notifier = {
  269. ibwdt_notify_sys,
  270. NULL,
  271. 0
  272. };
  273. static int __init
  274. ibwdt_init(void)
  275. {
  276. printk("WDT driver for IB700 single board computer initialising.n");
  277. spin_lock_init(&ibwdt_lock);
  278. misc_register(&ibwdt_miscdev);
  279. #if WDT_START != WDT_STOP
  280. request_region(WDT_STOP, 1, "IB700 WDT");
  281. #endif
  282. request_region(WDT_START, 1, "IB700 WDT");
  283. register_reboot_notifier(&ibwdt_notifier);
  284. return 0;
  285. }
  286. static void __exit
  287. ibwdt_exit(void)
  288. {
  289. misc_deregister(&ibwdt_miscdev);
  290. unregister_reboot_notifier(&ibwdt_notifier);
  291. #if WDT_START != WDT_STOP
  292. release_region(WDT_STOP,1);
  293. #endif
  294. release_region(WDT_START,1);
  295. }
  296. module_init(ibwdt_init);
  297. module_exit(ibwdt_exit);
  298. MODULE_AUTHOR("Charles Howes <chowes@vsol.net>");
  299. MODULE_DESCRIPTION("IB700 SBC watchdog driver");
  300. MODULE_LICENSE("GPL");
  301. /* end of ib700wdt.c */