cpu5wdt.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * sma cpu5 watchdog driver
  4. *
  5. * Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>
  6. */
  7. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  8. #include <linux/module.h>
  9. #include <linux/moduleparam.h>
  10. #include <linux/types.h>
  11. #include <linux/errno.h>
  12. #include <linux/miscdevice.h>
  13. #include <linux/fs.h>
  14. #include <linux/ioport.h>
  15. #include <linux/timer.h>
  16. #include <linux/completion.h>
  17. #include <linux/jiffies.h>
  18. #include <linux/io.h>
  19. #include <linux/uaccess.h>
  20. #include <linux/watchdog.h>
  21. /* adjustable parameters */
  22. static int verbose;
  23. static int port = 0x91;
  24. static int ticks = 10000;
  25. static DEFINE_SPINLOCK(cpu5wdt_lock);
  26. #define PFX "cpu5wdt: "
  27. #define CPU5WDT_EXTENT 0x0A
  28. #define CPU5WDT_STATUS_REG 0x00
  29. #define CPU5WDT_TIME_A_REG 0x02
  30. #define CPU5WDT_TIME_B_REG 0x03
  31. #define CPU5WDT_MODE_REG 0x04
  32. #define CPU5WDT_TRIGGER_REG 0x07
  33. #define CPU5WDT_ENABLE_REG 0x08
  34. #define CPU5WDT_RESET_REG 0x09
  35. #define CPU5WDT_INTERVAL (HZ/10+1)
  36. /* some device data */
  37. static struct {
  38. struct completion stop;
  39. int running;
  40. struct timer_list timer;
  41. int queue;
  42. int default_ticks;
  43. unsigned long inuse;
  44. } cpu5wdt_device;
  45. /* generic helper functions */
  46. static void cpu5wdt_trigger(struct timer_list *unused)
  47. {
  48. if (verbose > 2)
  49. pr_debug("trigger at %i ticks\n", ticks);
  50. if (cpu5wdt_device.running)
  51. ticks--;
  52. spin_lock(&cpu5wdt_lock);
  53. /* keep watchdog alive */
  54. outb(1, port + CPU5WDT_TRIGGER_REG);
  55. /* requeue?? */
  56. if (cpu5wdt_device.queue && ticks)
  57. mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
  58. else {
  59. /* ticks doesn't matter anyway */
  60. complete(&cpu5wdt_device.stop);
  61. }
  62. spin_unlock(&cpu5wdt_lock);
  63. }
  64. static void cpu5wdt_reset(void)
  65. {
  66. ticks = cpu5wdt_device.default_ticks;
  67. if (verbose)
  68. pr_debug("reset (%i ticks)\n", (int) ticks);
  69. }
  70. static void cpu5wdt_start(void)
  71. {
  72. unsigned long flags;
  73. spin_lock_irqsave(&cpu5wdt_lock, flags);
  74. if (!cpu5wdt_device.queue) {
  75. cpu5wdt_device.queue = 1;
  76. outb(0, port + CPU5WDT_TIME_A_REG);
  77. outb(0, port + CPU5WDT_TIME_B_REG);
  78. outb(1, port + CPU5WDT_MODE_REG);
  79. outb(0, port + CPU5WDT_RESET_REG);
  80. outb(0, port + CPU5WDT_ENABLE_REG);
  81. mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
  82. }
  83. /* if process dies, counter is not decremented */
  84. cpu5wdt_device.running++;
  85. spin_unlock_irqrestore(&cpu5wdt_lock, flags);
  86. }
  87. static int cpu5wdt_stop(void)
  88. {
  89. unsigned long flags;
  90. spin_lock_irqsave(&cpu5wdt_lock, flags);
  91. if (cpu5wdt_device.running)
  92. cpu5wdt_device.running = 0;
  93. ticks = cpu5wdt_device.default_ticks;
  94. spin_unlock_irqrestore(&cpu5wdt_lock, flags);
  95. if (verbose)
  96. pr_crit("stop not possible\n");
  97. return -EIO;
  98. }
  99. /* filesystem operations */
  100. static int cpu5wdt_open(struct inode *inode, struct file *file)
  101. {
  102. if (test_and_set_bit(0, &cpu5wdt_device.inuse))
  103. return -EBUSY;
  104. return stream_open(inode, file);
  105. }
  106. static int cpu5wdt_release(struct inode *inode, struct file *file)
  107. {
  108. clear_bit(0, &cpu5wdt_device.inuse);
  109. return 0;
  110. }
  111. static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
  112. unsigned long arg)
  113. {
  114. void __user *argp = (void __user *)arg;
  115. int __user *p = argp;
  116. unsigned int value;
  117. static const struct watchdog_info ident = {
  118. .options = WDIOF_CARDRESET,
  119. .identity = "CPU5 WDT",
  120. };
  121. switch (cmd) {
  122. case WDIOC_GETSUPPORT:
  123. if (copy_to_user(argp, &ident, sizeof(ident)))
  124. return -EFAULT;
  125. break;
  126. case WDIOC_GETSTATUS:
  127. value = inb(port + CPU5WDT_STATUS_REG);
  128. value = (value >> 2) & 1;
  129. return put_user(value, p);
  130. case WDIOC_GETBOOTSTATUS:
  131. return put_user(0, p);
  132. case WDIOC_SETOPTIONS:
  133. if (get_user(value, p))
  134. return -EFAULT;
  135. if (value & WDIOS_ENABLECARD)
  136. cpu5wdt_start();
  137. if (value & WDIOS_DISABLECARD)
  138. cpu5wdt_stop();
  139. break;
  140. case WDIOC_KEEPALIVE:
  141. cpu5wdt_reset();
  142. break;
  143. default:
  144. return -ENOTTY;
  145. }
  146. return 0;
  147. }
  148. static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,
  149. size_t count, loff_t *ppos)
  150. {
  151. if (!count)
  152. return -EIO;
  153. cpu5wdt_reset();
  154. return count;
  155. }
  156. static const struct file_operations cpu5wdt_fops = {
  157. .owner = THIS_MODULE,
  158. .llseek = no_llseek,
  159. .unlocked_ioctl = cpu5wdt_ioctl,
  160. .compat_ioctl = compat_ptr_ioctl,
  161. .open = cpu5wdt_open,
  162. .write = cpu5wdt_write,
  163. .release = cpu5wdt_release,
  164. };
  165. static struct miscdevice cpu5wdt_misc = {
  166. .minor = WATCHDOG_MINOR,
  167. .name = "watchdog",
  168. .fops = &cpu5wdt_fops,
  169. };
  170. /* init/exit function */
  171. static int cpu5wdt_init(void)
  172. {
  173. unsigned int val;
  174. int err;
  175. if (verbose)
  176. pr_debug("port=0x%x, verbose=%i\n", port, verbose);
  177. init_completion(&cpu5wdt_device.stop);
  178. cpu5wdt_device.queue = 0;
  179. timer_setup(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
  180. cpu5wdt_device.default_ticks = ticks;
  181. if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
  182. pr_err("request_region failed\n");
  183. err = -EBUSY;
  184. goto no_port;
  185. }
  186. /* watchdog reboot? */
  187. val = inb(port + CPU5WDT_STATUS_REG);
  188. val = (val >> 2) & 1;
  189. if (!val)
  190. pr_info("sorry, was my fault\n");
  191. err = misc_register(&cpu5wdt_misc);
  192. if (err < 0) {
  193. pr_err("misc_register failed\n");
  194. goto no_misc;
  195. }
  196. pr_info("init success\n");
  197. return 0;
  198. no_misc:
  199. release_region(port, CPU5WDT_EXTENT);
  200. no_port:
  201. return err;
  202. }
  203. static int cpu5wdt_init_module(void)
  204. {
  205. return cpu5wdt_init();
  206. }
  207. static void cpu5wdt_exit(void)
  208. {
  209. if (cpu5wdt_device.queue) {
  210. cpu5wdt_device.queue = 0;
  211. wait_for_completion(&cpu5wdt_device.stop);
  212. del_timer(&cpu5wdt_device.timer);
  213. }
  214. misc_deregister(&cpu5wdt_misc);
  215. release_region(port, CPU5WDT_EXTENT);
  216. }
  217. static void cpu5wdt_exit_module(void)
  218. {
  219. cpu5wdt_exit();
  220. }
  221. /* module entry points */
  222. module_init(cpu5wdt_init_module);
  223. module_exit(cpu5wdt_exit_module);
  224. MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
  225. MODULE_DESCRIPTION("sma cpu5 watchdog driver");
  226. MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog");
  227. MODULE_LICENSE("GPL");
  228. module_param_hw(port, int, ioport, 0);
  229. MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
  230. module_param(verbose, int, 0);
  231. MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
  232. module_param(ticks, int, 0);
  233. MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");