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

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * drivers/char/shwdt.c
  3.  *
  4.  * Watchdog driver for integrated watchdog in the SuperH processors.
  5.  *
  6.  * Copyright (C) 2001, 2002 Paul Mundt <lethal@0xd6.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.  * 19-Apr-2002 Rob Radez <rob@osinvestor.com>
  16.  *     Added expect close support, made emulated timeout runtime changeable
  17.  *     general cleanups, add some ioctls
  18.  */
  19. #include <linux/config.h>
  20. #include <linux/module.h>
  21. #include <linux/init.h>
  22. #include <linux/kernel.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 <asm/io.h>
  30. #include <asm/uaccess.h>
  31. #if defined(CONFIG_CPU_SH5)
  32.   #define WTCNT CPRC_BASE + 0x10
  33.   #define WTCSR CPRC_BASE + 0x18
  34. #elif defined(CONFIG_CPU_SH4)
  35.   #define WTCNT 0xffc00008
  36.   #define WTCSR 0xffc0000c
  37. #elif defined(CONFIG_CPU_SH3)
  38.   #define WTCNT 0xffffff84
  39.   #define WTCSR 0xffffff86
  40. #else
  41.   #error "Can't use SuperH watchdog on this platform"
  42. #endif
  43. #define WTCNT_HIGH 0x5a00
  44. #define WTCSR_HIGH 0xa500
  45. #define WTCSR_TME 0x80
  46. #define WTCSR_WT 0x40
  47. #define WTCSR_RSTS 0x20
  48. #define WTCSR_WOVF 0x10
  49. #define WTCSR_IOVF 0x08
  50. #define WTCSR_CKS2 0x04
  51. #define WTCSR_CKS1 0x02
  52. #define WTCSR_CKS0 0x01
  53. /*
  54.  * CKS0-2 supports a number of clock division ratios. At the time the watchdog
  55.  * is enabled, it defaults to a 41 usec overflow period .. we overload this to
  56.  * something a little more reasonable, and really can't deal with anything
  57.  * lower than WTCSR_CKS_1024, else we drop back into the usec range.
  58.  *
  59.  * Clock Division Ratio         Overflow Period
  60.  * --------------------------------------------
  61.  *     1/32 (initial value)       41 usecs
  62.  *     1/64                       82 usecs
  63.  *     1/128                     164 usecs
  64.  *     1/256                     328 usecs
  65.  *     1/512                     656 usecs
  66.  *     1/1024                   1.31 msecs
  67.  *     1/2048                   2.62 msecs
  68.  *     1/4096                   5.25 msecs
  69.  */
  70. #define WTCSR_CKS_32 0x00
  71. #define WTCSR_CKS_64 0x01
  72. #define WTCSR_CKS_128 0x02
  73. #define WTCSR_CKS_256 0x03
  74. #define WTCSR_CKS_512 0x04
  75. #define WTCSR_CKS_1024 0x05
  76. #define WTCSR_CKS_2048 0x06
  77. #define WTCSR_CKS_4096 0x07
  78. /*
  79.  * Default clock division ratio is 5.25 msecs. Overload this at module load
  80.  * time. Any value not in the msec range will default to a timeout of one
  81.  * jiffy, which exceeds the usec overflow periods.
  82.  */
  83. static int clock_division_ratio = WTCSR_CKS_4096;
  84. #define msecs_to_jiffies(msecs) (jiffies + ((HZ * msecs + 999) / 1000))
  85. #define next_ping_period(cks) msecs_to_jiffies(cks - 4)
  86. static unsigned long sh_is_open;
  87. static struct watchdog_info sh_wdt_info;
  88. static char shwdt_expect_close;
  89. static struct timer_list timer;
  90. static unsigned long next_heartbeat;
  91. static int sh_heartbeat = 30;
  92. #ifdef CONFIG_WATCHDOG_NOWAYOUT
  93. static int nowayout = 1;
  94. #else
  95. static int nowayout = 0;
  96. #endif
  97. /**
  98.  * sh_wdt_write_cnt - Write to Counter
  99.  *
  100.  * @val: Value to write
  101.  *
  102.  * Writes the given value @val to the lower byte of the timer counter.
  103.  * The upper byte is set manually on each write.
  104.  */
  105. static void sh_wdt_write_cnt(__u8 val)
  106. {
  107. ctrl_outw(WTCNT_HIGH | (__u16)val, WTCNT);
  108. }
  109. /**
  110.  *  sh_wdt_write_csr - Write to Control/Status Register
  111.  *
  112.  *  @val: Value to write
  113.  *
  114.  *  Writes the given value @val to the lower byte of the control/status
  115.  *  register. The upper byte is set manually on each write.
  116.  */
  117. static void sh_wdt_write_csr(__u8 val)
  118. {
  119. ctrl_outw(WTCSR_HIGH | (__u16)val, WTCSR);
  120. }
  121. /**
  122.  *  sh_wdt_start - Start the Watchdog
  123.  *
  124.  *  Starts the watchdog.
  125.  */
  126. static void sh_wdt_start(void)
  127. {
  128. timer.expires = next_ping_period(clock_division_ratio);
  129. next_heartbeat = jiffies + (sh_heartbeat * HZ);
  130. add_timer(&timer);
  131. sh_wdt_write_csr(WTCSR_WT | WTCSR_CKS_4096);
  132. sh_wdt_write_cnt(0);
  133. sh_wdt_write_csr((ctrl_inb(WTCSR) | WTCSR_TME));
  134. }
  135. /**
  136.  *  sh_wdt_stop - Stop the Watchdog
  137.  *
  138.  *  Stops the watchdog.
  139.  */
  140. static void sh_wdt_stop(void)
  141. {
  142. del_timer(&timer);
  143. sh_wdt_write_csr((ctrl_inb(WTCSR) & ~WTCSR_TME));
  144. }
  145. /**
  146.  *  sh_wdt_ping - Ping the Watchdog
  147.  *
  148.  * @data: Unused
  149.  *
  150.  *  Clears overflow bit, resets timer counter.
  151.  */
  152. static void sh_wdt_ping(unsigned long data)
  153. {
  154. if (time_before(jiffies, next_heartbeat)) {
  155. sh_wdt_write_csr((ctrl_inb(WTCSR) & ~WTCSR_IOVF));
  156. sh_wdt_write_cnt(0);
  157. timer.expires = next_ping_period(clock_division_ratio);
  158. add_timer(&timer);
  159. }
  160. }
  161. /**
  162.  *  sh_wdt_open - Open the Device
  163.  *
  164.  *  @inode: inode of device
  165.  *  @file: file handle of device
  166.  *
  167.  *  Watchdog device is opened and started.
  168.  */
  169. static int sh_wdt_open(struct inode *inode, struct file *file)
  170. {
  171. if (test_and_set_bit(0, &sh_is_open))
  172. return -EBUSY;
  173. sh_wdt_start();
  174. return 0;
  175. }
  176. /**
  177.  *  sh_wdt_close - Close the Device
  178.  *
  179.  *  @inode: inode of device
  180.  *  @file: file handle of device
  181.  *
  182.  *  Watchdog device is closed and stopped.
  183.  */
  184. static int sh_wdt_close(struct inode *inode, struct file *file)
  185. {
  186. if (!nowayout && shwdt_expect_close == 42) {
  187. sh_wdt_stop();
  188. } else {
  189. printk(KERN_CRIT "shwdt: Unexpected close, not stopping watchdog!n");
  190. next_heartbeat = jiffies + (sh_heartbeat * HZ);
  191. }
  192. clear_bit(0, &sh_is_open);
  193. shwdt_expect_close = 0;
  194. return 0;
  195. }
  196. /**
  197.  *  sh_wdt_write - Write to Device
  198.  *
  199.  *  @file: file handle of device
  200.  *  @buf: buffer to write
  201.  *  @count: length of buffer
  202.  *  @ppos: offset
  203.  *
  204.  *  Pings the watchdog on write.
  205.  */
  206. static ssize_t sh_wdt_write(struct file *file, const char *buf,
  207.     size_t count, loff_t *ppos)
  208. {
  209. /* Can't seek (pwrite) on this device */
  210. if (ppos != &file->f_pos)
  211. return -ESPIPE;
  212. if (count) {
  213. size_t i;
  214. shwdt_expect_close = 0;
  215. for (i = 0; i != count; i++) {
  216. char c;
  217. if (get_user(c, buf + i))
  218. return -EFAULT;
  219. if (c == 'V')
  220. shwdt_expect_close = 42;
  221. }
  222. next_heartbeat = jiffies + (sh_heartbeat * HZ);
  223. }
  224. return count;
  225. }
  226. /**
  227.  *  sh_wdt_ioctl - Query Device
  228.  *
  229.  *  @inode: inode of device
  230.  *  @file: file handle of device
  231.  *  @cmd: watchdog command
  232.  *  @arg: argument
  233.  *
  234.  *  Query basic information from the device or ping it, as outlined by the
  235.  *  watchdog API.
  236.  */
  237. static int sh_wdt_ioctl(struct inode *inode, struct file *file,
  238. unsigned int cmd, unsigned long arg)
  239. {
  240. int new_timeout;
  241. switch (cmd) {
  242. case WDIOC_GETSUPPORT:
  243. if (copy_to_user((struct watchdog_info *)arg,
  244.   &sh_wdt_info,
  245.   sizeof(sh_wdt_info))) {
  246. return -EFAULT;
  247. }
  248. break;
  249. case WDIOC_GETSTATUS:
  250. case WDIOC_GETBOOTSTATUS:
  251. return put_user(0, (int *)arg);
  252. case WDIOC_KEEPALIVE:
  253. next_heartbeat = jiffies + (sh_heartbeat * HZ);
  254. break;
  255. case WDIOC_SETTIMEOUT:
  256. if (get_user(new_timeout, (int *)arg))
  257. return -EFAULT;
  258. if (new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */
  259. return -EINVAL;
  260. sh_heartbeat = new_timeout;
  261. next_heartbeat = jiffies + (sh_heartbeat * HZ);
  262. /* Fall */
  263. case WDIOC_GETTIMEOUT:
  264. return put_user(sh_heartbeat, (int *)arg);
  265. case WDIOC_SETOPTIONS:
  266. {
  267. int options, retval = -EINVAL;
  268. if (get_user(options, (int *)arg))
  269. return -EFAULT;
  270. if (options & WDIOS_DISABLECARD) {
  271. sh_wdt_stop();
  272. retval = 0;
  273. }
  274. if (options & WDIOS_ENABLECARD) {
  275. sh_wdt_start();
  276. retval = 0;
  277. }
  278. return retval;
  279. }
  280. default:
  281. return -ENOTTY;
  282. }
  283. return 0;
  284. }
  285. /**
  286.  *  sh_wdt_notify_sys - Notifier Handler
  287.  * 
  288.  *  @this: notifier block
  289.  *  @code: notifier event
  290.  *  @unused: unused
  291.  *
  292.  *  Handles specific events, such as turning off the watchdog during a
  293.  *  shutdown event.
  294.  */
  295. static int sh_wdt_notify_sys(struct notifier_block *this,
  296.      unsigned long code, void *unused)
  297. {
  298. if (code == SYS_DOWN || code == SYS_HALT) {
  299. sh_wdt_stop();
  300. }
  301. return NOTIFY_DONE;
  302. }
  303. static struct file_operations sh_wdt_fops = {
  304. owner: THIS_MODULE,
  305. llseek: no_llseek,
  306. write: sh_wdt_write,
  307. ioctl: sh_wdt_ioctl,
  308. open: sh_wdt_open,
  309. release: sh_wdt_close,
  310. };
  311. static struct watchdog_info sh_wdt_info = {
  312. options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
  313. firmware_version: 0,
  314. identity: "SH WDT",
  315. };
  316. static struct notifier_block sh_wdt_notifier = {
  317. sh_wdt_notify_sys,
  318. NULL,
  319. 0
  320. };
  321. static struct miscdevice sh_wdt_miscdev = {
  322. minor: WATCHDOG_MINOR,
  323. name: "watchdog",
  324. fops: &sh_wdt_fops,
  325. };
  326. /**
  327.  *  sh_wdt_init - Initialize module
  328.  *
  329.  *  Registers the device and notifier handler. Actual device
  330.  *  initialization is handled by sh_wdt_open().
  331.  */
  332. static int __init sh_wdt_init(void)
  333. {
  334. if (misc_register(&sh_wdt_miscdev)) {
  335. printk(KERN_ERR "shwdt: Can't register misc devicen");
  336. return -EINVAL;
  337. }
  338. if (!request_region(WTCNT, 1, "shwdt")) {
  339. printk(KERN_ERR "shwdt: Can't request WTCNT regionn");
  340. misc_deregister(&sh_wdt_miscdev);
  341. return -ENXIO;
  342. }
  343. if (!request_region(WTCSR, 1, "shwdt")) {
  344. printk(KERN_ERR "shwdt: Can't request WTCSR regionn");
  345. release_region(WTCNT, 1);
  346. misc_deregister(&sh_wdt_miscdev);
  347. return -ENXIO;
  348. }
  349. if (register_reboot_notifier(&sh_wdt_notifier)) {
  350. printk(KERN_ERR "shwdt: Can't register reboot notifiern");
  351. release_region(WTCSR, 1);
  352. release_region(WTCNT, 1);
  353. misc_deregister(&sh_wdt_miscdev);
  354. return -EINVAL;
  355. }
  356. init_timer(&timer);
  357. timer.function = sh_wdt_ping;
  358. timer.data = 0;
  359. return 0;
  360. }
  361. /**
  362.  *  sh_wdt_exit - Deinitialize module
  363.  *
  364.  *  Unregisters the device and notifier handler. Actual device
  365.  *  deinitialization is handled by sh_wdt_close().
  366.  */
  367. static void __exit sh_wdt_exit(void)
  368. {
  369. unregister_reboot_notifier(&sh_wdt_notifier);
  370. release_region(WTCSR, 1);
  371. release_region(WTCNT, 1);
  372. misc_deregister(&sh_wdt_miscdev);
  373. }
  374. EXPORT_NO_SYMBOLS;
  375. MODULE_PARM(nowayout,"i");
  376. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
  377. MODULE_PARM(clock_division_ratio, "i");
  378. MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7.");
  379. MODULE_AUTHOR("Paul Mundt <lethal@0xd6.org>");
  380. MODULE_DESCRIPTION("SuperH watchdog driver");
  381. MODULE_LICENSE("GPL");
  382. module_init(sh_wdt_init);
  383. module_exit(sh_wdt_exit);