pika_wdt.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * PIKA FPGA based Watchdog Timer
  3. *
  4. * Copyright (c) 2008 PIKA Technologies
  5. * Sean MacLennan <smaclennan@pikatech.com>
  6. */
  7. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  8. #include <linux/init.h>
  9. #include <linux/errno.h>
  10. #include <linux/module.h>
  11. #include <linux/moduleparam.h>
  12. #include <linux/types.h>
  13. #include <linux/kernel.h>
  14. #include <linux/fs.h>
  15. #include <linux/miscdevice.h>
  16. #include <linux/watchdog.h>
  17. #include <linux/reboot.h>
  18. #include <linux/jiffies.h>
  19. #include <linux/timer.h>
  20. #include <linux/bitops.h>
  21. #include <linux/uaccess.h>
  22. #include <linux/io.h>
  23. #include <linux/of_address.h>
  24. #include <linux/of_platform.h>
  25. #define DRV_NAME "PIKA-WDT"
  26. /* Hardware timeout in seconds */
  27. #define WDT_HW_TIMEOUT 2
  28. /* Timer heartbeat (500ms) */
  29. #define WDT_TIMEOUT (HZ/2)
  30. /* User land timeout */
  31. #define WDT_HEARTBEAT 15
  32. static int heartbeat = WDT_HEARTBEAT;
  33. module_param(heartbeat, int, 0);
  34. MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
  35. "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
  36. static bool nowayout = WATCHDOG_NOWAYOUT;
  37. module_param(nowayout, bool, 0);
  38. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
  39. "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  40. static struct {
  41. void __iomem *fpga;
  42. unsigned long next_heartbeat; /* the next_heartbeat for the timer */
  43. unsigned long open;
  44. char expect_close;
  45. int bootstatus;
  46. struct timer_list timer; /* The timer that pings the watchdog */
  47. } pikawdt_private;
  48. static struct watchdog_info ident = {
  49. .identity = DRV_NAME,
  50. .options = WDIOF_CARDRESET |
  51. WDIOF_SETTIMEOUT |
  52. WDIOF_KEEPALIVEPING |
  53. WDIOF_MAGICCLOSE,
  54. };
  55. /*
  56. * Reload the watchdog timer. (ie, pat the watchdog)
  57. */
  58. static inline void pikawdt_reset(void)
  59. {
  60. /* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) --
  61. * Bit 7, WTCHDG_EN: When set to 1, the watchdog timer is enabled.
  62. * Once enabled, it cannot be disabled. The watchdog can be
  63. * kicked by performing any write access to the reset
  64. * control register (this register).
  65. * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in
  66. * seconds. Valid ranges are 1 to 15 seconds. The value can
  67. * be modified dynamically.
  68. */
  69. unsigned reset = in_be32(pikawdt_private.fpga + 0x14);
  70. /* enable with max timeout - 15 seconds */
  71. reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8);
  72. out_be32(pikawdt_private.fpga + 0x14, reset);
  73. }
  74. /*
  75. * Timer tick
  76. */
  77. static void pikawdt_ping(unsigned long data)
  78. {
  79. if (time_before(jiffies, pikawdt_private.next_heartbeat) ||
  80. (!nowayout && !pikawdt_private.open)) {
  81. pikawdt_reset();
  82. mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
  83. } else
  84. pr_crit("I will reset your machine !\n");
  85. }
  86. static void pikawdt_keepalive(void)
  87. {
  88. pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ;
  89. }
  90. static void pikawdt_start(void)
  91. {
  92. pikawdt_keepalive();
  93. mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
  94. }
  95. /*
  96. * Watchdog device is opened, and watchdog starts running.
  97. */
  98. static int pikawdt_open(struct inode *inode, struct file *file)
  99. {
  100. /* /dev/watchdog can only be opened once */
  101. if (test_and_set_bit(0, &pikawdt_private.open))
  102. return -EBUSY;
  103. pikawdt_start();
  104. return nonseekable_open(inode, file);
  105. }
  106. /*
  107. * Close the watchdog device.
  108. */
  109. static int pikawdt_release(struct inode *inode, struct file *file)
  110. {
  111. /* stop internal ping */
  112. if (!pikawdt_private.expect_close)
  113. del_timer(&pikawdt_private.timer);
  114. clear_bit(0, &pikawdt_private.open);
  115. pikawdt_private.expect_close = 0;
  116. return 0;
  117. }
  118. /*
  119. * Pat the watchdog whenever device is written to.
  120. */
  121. static ssize_t pikawdt_write(struct file *file, const char __user *data,
  122. size_t len, loff_t *ppos)
  123. {
  124. if (!len)
  125. return 0;
  126. /* Scan for magic character */
  127. if (!nowayout) {
  128. size_t i;
  129. pikawdt_private.expect_close = 0;
  130. for (i = 0; i < len; i++) {
  131. char c;
  132. if (get_user(c, data + i))
  133. return -EFAULT;
  134. if (c == 'V') {
  135. pikawdt_private.expect_close = 42;
  136. break;
  137. }
  138. }
  139. }
  140. pikawdt_keepalive();
  141. return len;
  142. }
  143. /*
  144. * Handle commands from user-space.
  145. */
  146. static long pikawdt_ioctl(struct file *file,
  147. unsigned int cmd, unsigned long arg)
  148. {
  149. void __user *argp = (void __user *)arg;
  150. int __user *p = argp;
  151. int new_value;
  152. switch (cmd) {
  153. case WDIOC_GETSUPPORT:
  154. return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
  155. case WDIOC_GETSTATUS:
  156. return put_user(0, p);
  157. case WDIOC_GETBOOTSTATUS:
  158. return put_user(pikawdt_private.bootstatus, p);
  159. case WDIOC_KEEPALIVE:
  160. pikawdt_keepalive();
  161. return 0;
  162. case WDIOC_SETTIMEOUT:
  163. if (get_user(new_value, p))
  164. return -EFAULT;
  165. heartbeat = new_value;
  166. pikawdt_keepalive();
  167. return put_user(new_value, p); /* return current value */
  168. case WDIOC_GETTIMEOUT:
  169. return put_user(heartbeat, p);
  170. }
  171. return -ENOTTY;
  172. }
  173. static const struct file_operations pikawdt_fops = {
  174. .owner = THIS_MODULE,
  175. .llseek = no_llseek,
  176. .open = pikawdt_open,
  177. .release = pikawdt_release,
  178. .write = pikawdt_write,
  179. .unlocked_ioctl = pikawdt_ioctl,
  180. };
  181. static struct miscdevice pikawdt_miscdev = {
  182. .minor = WATCHDOG_MINOR,
  183. .name = "watchdog",
  184. .fops = &pikawdt_fops,
  185. };
  186. static int __init pikawdt_init(void)
  187. {
  188. struct device_node *np;
  189. void __iomem *fpga;
  190. static u32 post1;
  191. int ret;
  192. np = of_find_compatible_node(NULL, NULL, "pika,fpga");
  193. if (np == NULL) {
  194. pr_err("Unable to find fpga\n");
  195. return -ENOENT;
  196. }
  197. pikawdt_private.fpga = of_iomap(np, 0);
  198. of_node_put(np);
  199. if (pikawdt_private.fpga == NULL) {
  200. pr_err("Unable to map fpga\n");
  201. return -ENOMEM;
  202. }
  203. ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff;
  204. /* POST information is in the sd area. */
  205. np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd");
  206. if (np == NULL) {
  207. pr_err("Unable to find fpga-sd\n");
  208. ret = -ENOENT;
  209. goto out;
  210. }
  211. fpga = of_iomap(np, 0);
  212. of_node_put(np);
  213. if (fpga == NULL) {
  214. pr_err("Unable to map fpga-sd\n");
  215. ret = -ENOMEM;
  216. goto out;
  217. }
  218. /* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) --
  219. * Bit 31, WDOG: Set to 1 when the last reset was caused by a watchdog
  220. * timeout.
  221. */
  222. post1 = in_be32(fpga + 0x40);
  223. if (post1 & 0x80000000)
  224. pikawdt_private.bootstatus = WDIOF_CARDRESET;
  225. iounmap(fpga);
  226. setup_timer(&pikawdt_private.timer, pikawdt_ping, 0);
  227. ret = misc_register(&pikawdt_miscdev);
  228. if (ret) {
  229. pr_err("Unable to register miscdev\n");
  230. goto out;
  231. }
  232. pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
  233. heartbeat, nowayout);
  234. return 0;
  235. out:
  236. iounmap(pikawdt_private.fpga);
  237. return ret;
  238. }
  239. static void __exit pikawdt_exit(void)
  240. {
  241. misc_deregister(&pikawdt_miscdev);
  242. iounmap(pikawdt_private.fpga);
  243. }
  244. module_init(pikawdt_init);
  245. module_exit(pikawdt_exit);
  246. MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>");
  247. MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer");
  248. MODULE_LICENSE("GPL");