pika_wdt.c 6.8 KB

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