/drivers/char/watchdog/ixp2000_wdt.c

https://bitbucket.org/evzijst/gittest · C · 219 lines · 157 code · 44 blank · 18 comment · 14 complexity · c6cf7fd73e1b1ec41f4dd78fbf33ad95 MD5 · raw file

  1. /*
  2. * drivers/watchdog/ixp2000_wdt.c
  3. *
  4. * Watchdog driver for Intel IXP2000 network processors
  5. *
  6. * Adapted from the IXP4xx watchdog driver by Lennert Buytenhek.
  7. * The original version carries these notices:
  8. *
  9. * Author: Deepak Saxena <dsaxena@plexity.net>
  10. *
  11. * Copyright 2004 (c) MontaVista, Software, Inc.
  12. * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
  13. *
  14. * This file is licensed under the terms of the GNU General Public
  15. * License version 2. This program is licensed "as is" without any
  16. * warranty of any kind, whether express or implied.
  17. */
  18. #include <linux/config.h>
  19. #include <linux/module.h>
  20. #include <linux/moduleparam.h>
  21. #include <linux/types.h>
  22. #include <linux/kernel.h>
  23. #include <linux/fs.h>
  24. #include <linux/miscdevice.h>
  25. #include <linux/watchdog.h>
  26. #include <linux/init.h>
  27. #include <linux/bitops.h>
  28. #include <asm/hardware.h>
  29. #include <asm/uaccess.h>
  30. #ifdef CONFIG_WATCHDOG_NOWAYOUT
  31. static int nowayout = 1;
  32. #else
  33. static int nowayout = 0;
  34. #endif
  35. static unsigned int heartbeat = 60; /* (secs) Default is 1 minute */
  36. static unsigned long wdt_status;
  37. #define WDT_IN_USE 0
  38. #define WDT_OK_TO_CLOSE 1
  39. static unsigned long wdt_tick_rate;
  40. static void
  41. wdt_enable(void)
  42. {
  43. ixp2000_reg_write(IXP2000_RESET0, *(IXP2000_RESET0) | WDT_RESET_ENABLE);
  44. ixp2000_reg_write(IXP2000_TWDE, WDT_ENABLE);
  45. ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
  46. ixp2000_reg_write(IXP2000_T4_CTL, TIMER_DIVIDER_256 | TIMER_ENABLE);
  47. }
  48. static void
  49. wdt_disable(void)
  50. {
  51. ixp2000_reg_write(IXP2000_T4_CTL, 0);
  52. }
  53. static void
  54. wdt_keepalive(void)
  55. {
  56. ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
  57. }
  58. static int
  59. ixp2000_wdt_open(struct inode *inode, struct file *file)
  60. {
  61. if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  62. return -EBUSY;
  63. clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  64. wdt_enable();
  65. return nonseekable_open(inode, file);
  66. }
  67. static ssize_t
  68. ixp2000_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
  69. {
  70. if (len) {
  71. if (!nowayout) {
  72. size_t i;
  73. clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  74. for (i = 0; i != len; i++) {
  75. char c;
  76. if (get_user(c, data + i))
  77. return -EFAULT;
  78. if (c == 'V')
  79. set_bit(WDT_OK_TO_CLOSE, &wdt_status);
  80. }
  81. }
  82. wdt_keepalive();
  83. }
  84. return len;
  85. }
  86. static struct watchdog_info ident = {
  87. .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
  88. WDIOF_KEEPALIVEPING,
  89. .identity = "IXP2000 Watchdog",
  90. };
  91. static int
  92. ixp2000_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  93. unsigned long arg)
  94. {
  95. int ret = -ENOIOCTLCMD;
  96. int time;
  97. switch (cmd) {
  98. case WDIOC_GETSUPPORT:
  99. ret = copy_to_user((struct watchdog_info *)arg, &ident,
  100. sizeof(ident)) ? -EFAULT : 0;
  101. break;
  102. case WDIOC_GETSTATUS:
  103. ret = put_user(0, (int *)arg);
  104. break;
  105. case WDIOC_GETBOOTSTATUS:
  106. ret = put_user(0, (int *)arg);
  107. break;
  108. case WDIOC_SETTIMEOUT:
  109. ret = get_user(time, (int *)arg);
  110. if (ret)
  111. break;
  112. if (time <= 0 || time > 60) {
  113. ret = -EINVAL;
  114. break;
  115. }
  116. heartbeat = time;
  117. wdt_keepalive();
  118. /* Fall through */
  119. case WDIOC_GETTIMEOUT:
  120. ret = put_user(heartbeat, (int *)arg);
  121. break;
  122. case WDIOC_KEEPALIVE:
  123. wdt_enable();
  124. ret = 0;
  125. break;
  126. }
  127. return ret;
  128. }
  129. static int
  130. ixp2000_wdt_release(struct inode *inode, struct file *file)
  131. {
  132. if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
  133. wdt_disable();
  134. } else {
  135. printk(KERN_CRIT "WATCHDOG: Device closed unexpectdly - "
  136. "timer will not stop\n");
  137. }
  138. clear_bit(WDT_IN_USE, &wdt_status);
  139. clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  140. return 0;
  141. }
  142. static struct file_operations ixp2000_wdt_fops =
  143. {
  144. .owner = THIS_MODULE,
  145. .llseek = no_llseek,
  146. .write = ixp2000_wdt_write,
  147. .ioctl = ixp2000_wdt_ioctl,
  148. .open = ixp2000_wdt_open,
  149. .release = ixp2000_wdt_release,
  150. };
  151. static struct miscdevice ixp2000_wdt_miscdev =
  152. {
  153. .minor = WATCHDOG_MINOR,
  154. .name = "IXP2000 Watchdog",
  155. .fops = &ixp2000_wdt_fops,
  156. };
  157. static int __init ixp2000_wdt_init(void)
  158. {
  159. wdt_tick_rate = (*IXP2000_T1_CLD * HZ)/ 256;;
  160. return misc_register(&ixp2000_wdt_miscdev);
  161. }
  162. static void __exit ixp2000_wdt_exit(void)
  163. {
  164. misc_deregister(&ixp2000_wdt_miscdev);
  165. }
  166. module_init(ixp2000_wdt_init);
  167. module_exit(ixp2000_wdt_exit);
  168. MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net">);
  169. MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog");
  170. module_param(heartbeat, int, 0);
  171. MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
  172. module_param(nowayout, int, 0);
  173. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
  174. MODULE_LICENSE("GPL");
  175. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);