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

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * ALi M7101 PMU Computer Watchdog Timer driver for Linux 2.4.x
  3.  *
  4.  * Based on w83877f_wdt.c by Scott Jennings <management@oro.net>
  5.  * and the Cobalt kernel WDT timer driver by Tim Hockin
  6.  *                                       <thockin@cobaltnet.com>
  7.  *
  8.  * (c)2002 Steve Hill <steve@navaho.co.uk>
  9.  * 
  10.  *  Theory of operation:
  11.  *  A Watchdog Timer (WDT) is a hardware circuit that can 
  12.  *  reset the computer system in case of a software fault.
  13.  *  You probably knew that already.
  14.  *
  15.  *  Usually a userspace daemon will notify the kernel WDT driver
  16.  *  via the /proc/watchdog special device file that userspace is
  17.  *  still alive, at regular intervals.  When such a notification
  18.  *  occurs, the driver will usually tell the hardware watchdog
  19.  *  that everything is in order, and that the watchdog should wait
  20.  *  for yet another little while to reset the system.
  21.  *  If userspace fails (RAM error, kernel bug, whatever), the
  22.  *  notifications cease to occur, and the hardware watchdog will
  23.  *  reset the system (causing a reboot) after the timeout occurs.
  24.  *
  25.  *  This WDT driver is different from most other Linux WDT
  26.  *  drivers in that the driver will ping the watchdog by itself,
  27.  *  because this particular WDT has a very short timeout (1.6
  28.  *  seconds) and it would be insane to count on any userspace
  29.  *  daemon always getting scheduled within that time frame.
  30.  */
  31. #include <linux/module.h>
  32. #include <linux/version.h>
  33. #include <linux/types.h>
  34. #include <linux/errno.h>
  35. #include <linux/kernel.h>
  36. #include <linux/timer.h>
  37. #include <linux/sched.h>
  38. #include <linux/miscdevice.h>
  39. #include <linux/watchdog.h>
  40. #include <linux/slab.h>
  41. #include <linux/ioport.h>
  42. #include <linux/fcntl.h>
  43. #include <linux/smp_lock.h>
  44. #include <asm/io.h>
  45. #include <asm/uaccess.h>
  46. #include <asm/system.h>
  47. #include <linux/notifier.h>
  48. #include <linux/reboot.h>
  49. #include <linux/init.h>
  50. #include <linux/pci.h>
  51. #define OUR_NAME "alim7101_wdt"
  52. #define WDT_ENABLE 0x9C
  53. #define WDT_DISABLE 0x8C
  54. #define ALI_7101_WDT    0x92
  55. #define ALI_WDT_ARM     0x01
  56. /*
  57.  * We're going to use a 1 second timeout.
  58.  * If we reset the watchdog every ~250ms we should be safe.  */
  59. #define WDT_INTERVAL (HZ/4+1)
  60. /*
  61.  * We must not require too good response from the userspace daemon.
  62.  * Here we require the userspace daemon to send us a heartbeat
  63.  * char to /dev/watchdog every 30 seconds.
  64.  */
  65. #define WDT_HEARTBEAT (HZ * 30)
  66. static void wdt_timer_ping(unsigned long);
  67. static struct timer_list timer;
  68. static unsigned long next_heartbeat;
  69. static unsigned long wdt_is_open;
  70. static int wdt_expect_close;
  71. static struct pci_dev *alim7101_pmu;
  72. #ifdef CONFIG_WATCHDOG_NOWAYOUT
  73. static int nowayout = 1;
  74. #else
  75. static int nowayout = 0;
  76. #endif
  77. MODULE_PARM(nowayout,"i");
  78. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
  79. /*
  80.  * Whack the dog
  81.  */
  82. static void wdt_timer_ping(unsigned long data)
  83. {
  84. /* If we got a heartbeat pulse within the WDT_US_INTERVAL
  85.  * we agree to ping the WDT 
  86.  */
  87. char tmp;
  88. if(time_before(jiffies, next_heartbeat)) 
  89. {
  90. /* Ping the WDT (this is actually a disarm/arm sequence) */
  91. pci_read_config_byte(alim7101_pmu, 0x92, &tmp);
  92. pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM));
  93. pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM));
  94. } else {
  95. printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdogn");
  96. }
  97. /* Re-set the timer interval */
  98. timer.expires = jiffies + WDT_INTERVAL;
  99. add_timer(&timer);
  100. }
  101. /* 
  102.  * Utility routines
  103.  */
  104. static void wdt_change(int writeval)
  105. {
  106. char tmp;
  107. pci_read_config_byte(alim7101_pmu, 0x92, &tmp);
  108. if (writeval == WDT_ENABLE)
  109. pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM));
  110. else
  111. pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM));
  112. }
  113. static void wdt_startup(void)
  114. {
  115. next_heartbeat = jiffies + WDT_HEARTBEAT;
  116. /* We must enable before we kick off the timer in case the timer
  117.    occurs as we ping it */
  118. wdt_change(WDT_ENABLE);
  119. /* Start the timer */
  120. timer.expires = jiffies + WDT_INTERVAL;
  121. add_timer(&timer);
  122. printk(OUR_NAME ": Watchdog timer is now enabled.n");  
  123. }
  124. static void wdt_turnoff(void)
  125. {
  126. /* Stop the timer */
  127. del_timer_sync(&timer);
  128. wdt_change(WDT_DISABLE);
  129. printk(OUR_NAME ": Watchdog timer is now disabled...n");
  130. }
  131. /*
  132.  * /dev/watchdog handling
  133.  */
  134. static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
  135. {
  136. /* We can't seek */
  137. if(ppos != &file->f_pos)
  138. return -ESPIPE;
  139. /* See if we got the magic character */
  140. if(count) 
  141. {
  142. if (!nowayout) {
  143. size_t ofs;
  144. /* note: just in case someone wrote the magic character
  145.  * five months ago... */
  146. wdt_expect_close = 0;
  147. /* now scan */
  148. for(ofs = 0; ofs != count; ofs++)
  149. {
  150. char c;
  151. if(get_user(c, buf+ofs))
  152. return -EFAULT;
  153. if(c == 'V')
  154. wdt_expect_close = 1;
  155. }
  156. }
  157. /* someone wrote to us, we should restart timer */
  158. next_heartbeat = jiffies + WDT_HEARTBEAT;
  159. return 1;
  160. };
  161. return 0;
  162. }
  163. static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos)
  164. {
  165. /* No can do */
  166. return -EINVAL;
  167. }
  168. static int fop_open(struct inode * inode, struct file * file)
  169. {
  170. /* Just in case we're already talking to someone... */
  171. if(test_and_set_bit(0, &wdt_is_open))
  172. return -EBUSY;
  173. /* Good, fire up the show */
  174. wdt_startup();
  175. return 0;
  176. }
  177. static int fop_close(struct inode * inode, struct file * file)
  178. {
  179. if(wdt_expect_close)
  180. wdt_turnoff();
  181. else {
  182. printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!n");
  183. }
  184. clear_bit(0, &wdt_is_open);
  185. return 0;
  186. }
  187. static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
  188. {
  189. static struct watchdog_info ident=
  190. {
  191. WDIOF_MAGICCLOSE,
  192. 1,
  193. "ALiM7101"
  194. };
  195. switch(cmd)
  196. {
  197. case WDIOC_GETSUPPORT:
  198. return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
  199. case WDIOC_KEEPALIVE:
  200. next_heartbeat = jiffies + WDT_HEARTBEAT;
  201. return 0;
  202. default:
  203. return -ENOTTY;
  204. }
  205. }
  206. static struct file_operations wdt_fops = {
  207. owner: THIS_MODULE,
  208. llseek: no_llseek,
  209. read: fop_read,
  210. write: fop_write,
  211. open: fop_open,
  212. release: fop_close,
  213. ioctl: fop_ioctl
  214. };
  215. static struct miscdevice wdt_miscdev = {
  216. WATCHDOG_MINOR,
  217. "watchdog",
  218. &wdt_fops
  219. };
  220. /*
  221.  * Notifier for system down
  222.  */
  223. static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
  224. {
  225. if (code==SYS_DOWN || code==SYS_HALT) wdt_turnoff();
  226. if (code==SYS_RESTART) {
  227. /*
  228.  * Cobalt devices have no way of rebooting themselves other than
  229.  * getting the watchdog to pull reset, so we restart the watchdog on
  230.  * reboot with no heartbeat
  231.  */
  232. wdt_change(WDT_ENABLE);
  233. printk(OUR_NAME ": Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second.n");
  234. };
  235. return NOTIFY_DONE;
  236. }
  237.  
  238. /*
  239.  * The WDT needs to learn about soft shutdowns in order to
  240.  * turn the timebomb registers off. 
  241.  */
  242.  
  243. static struct notifier_block wdt_notifier=
  244. {
  245. wdt_notify_sys,
  246. 0,
  247. 0
  248. };
  249. static void __exit alim7101_wdt_unload(void)
  250. {
  251. wdt_turnoff();
  252. /* Deregister */
  253. misc_deregister(&wdt_miscdev);
  254. unregister_reboot_notifier(&wdt_notifier);
  255. }
  256. static int __init alim7101_wdt_init(void)
  257. {
  258. int rc = -EBUSY;
  259. struct pci_dev *ali1543_south;
  260. char tmp;
  261. printk(KERN_INFO OUR_NAME ": Steve Hill <steve@navaho.co.uk>.n");
  262. alim7101_pmu = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,NULL);
  263. if (!alim7101_pmu) {
  264. printk(KERN_INFO OUR_NAME ": ALi M7101 PMU not present - WDT not setn");
  265. return -EBUSY;
  266. };
  267. /* Set the WDT in the PMU to 1 second */
  268. pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, 0x02);
  269. ali1543_south = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL);
  270. if (!ali1543_south) {
  271. printk(KERN_INFO OUR_NAME ": ALi 1543 South-Bridge not present - WDT not setn");
  272. return -EBUSY;
  273. };
  274. pci_read_config_byte(ali1543_south, 0x5e, &tmp);
  275. if ((tmp & 0x1e) != 0x12) {
  276. printk(KERN_INFO OUR_NAME ": ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not setn");
  277. return -EBUSY;
  278. };
  279. init_timer(&timer);
  280. timer.function = wdt_timer_ping;
  281. timer.data = 1;
  282. rc = misc_register(&wdt_miscdev);
  283. if (rc)
  284. return rc;
  285. rc = register_reboot_notifier(&wdt_notifier);
  286. if (rc) {
  287. misc_deregister(&wdt_miscdev);
  288. return rc;
  289. };
  290. printk(KERN_INFO OUR_NAME ": WDT driver for ALi M7101 initialised.n");
  291. return 0;
  292. }
  293. module_init(alim7101_wdt_init);
  294. module_exit(alim7101_wdt_unload);
  295. EXPORT_NO_SYMBOLS;
  296. MODULE_AUTHOR("Steve Hill");
  297. MODULE_LICENSE("GPL");