avila-wdt.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * avila-wdt.c
  3. * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
  4. *
  5. * based on:
  6. * drivers/char/watchdog/ixp4xx_wdt.c
  7. *
  8. * Watchdog driver for Intel IXP4xx network processors
  9. *
  10. * Author: Deepak Saxena <dsaxena@plexity.net>
  11. *
  12. * Copyright 2004 (c) MontaVista, Software, Inc.
  13. * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
  14. *
  15. * This file is licensed under the terms of the GNU General Public
  16. * License version 2. This program is licensed "as is" without any
  17. * warranty of any kind, whether express or implied.
  18. */
  19. #include <linux/module.h>
  20. #include <linux/moduleparam.h>
  21. #include <linux/types.h>
  22. #include <linux/kernel.h>
  23. #include <linux/jiffies.h>
  24. #include <linux/timer.h>
  25. #include <linux/fs.h>
  26. #include <linux/miscdevice.h>
  27. #include <linux/watchdog.h>
  28. #include <linux/init.h>
  29. #include <linux/bitops.h>
  30. #include <linux/uaccess.h>
  31. #include <mach/hardware.h>
  32. static int nowayout = WATCHDOG_NOWAYOUT;
  33. static int heartbeat = 20; /* (secs) Default is 20 seconds */
  34. static unsigned long wdt_status;
  35. static atomic_t wdt_counter;
  36. struct timer_list wdt_timer;
  37. #define WDT_IN_USE 0
  38. #define WDT_OK_TO_CLOSE 1
  39. #define WDT_RUNNING 2
  40. static void wdt_refresh(unsigned long data)
  41. {
  42. if (test_bit(WDT_RUNNING, &wdt_status)) {
  43. if (atomic_dec_and_test(&wdt_counter)) {
  44. printk(KERN_WARNING "Avila watchdog expired, expect a reboot soon!\n");
  45. clear_bit(WDT_RUNNING, &wdt_status);
  46. return;
  47. }
  48. }
  49. /* strobe to the watchdog */
  50. gpio_line_set(14, IXP4XX_GPIO_HIGH);
  51. gpio_line_set(14, IXP4XX_GPIO_LOW);
  52. mod_timer(&wdt_timer, jiffies + msecs_to_jiffies(500));
  53. }
  54. static void wdt_enable(void)
  55. {
  56. atomic_set(&wdt_counter, heartbeat * 2);
  57. /* Disable clock generator output on GPIO 14/15 */
  58. *IXP4XX_GPIO_GPCLKR &= ~(1 << 8);
  59. /* activate GPIO 14 out */
  60. gpio_line_config(14, IXP4XX_GPIO_OUT);
  61. gpio_line_set(14, IXP4XX_GPIO_LOW);
  62. if (!test_bit(WDT_RUNNING, &wdt_status))
  63. wdt_refresh(0);
  64. set_bit(WDT_RUNNING, &wdt_status);
  65. }
  66. static void wdt_disable(void)
  67. {
  68. /* Re-enable clock generator output on GPIO 14/15 */
  69. *IXP4XX_GPIO_GPCLKR |= (1 << 8);
  70. }
  71. static int avila_wdt_open(struct inode *inode, struct file *file)
  72. {
  73. if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  74. return -EBUSY;
  75. clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  76. wdt_enable();
  77. return nonseekable_open(inode, file);
  78. }
  79. static ssize_t
  80. avila_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
  81. {
  82. if (len) {
  83. if (!nowayout) {
  84. size_t i;
  85. clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  86. for (i = 0; i != len; i++) {
  87. char c;
  88. if (get_user(c, data + i))
  89. return -EFAULT;
  90. if (c == 'V')
  91. set_bit(WDT_OK_TO_CLOSE, &wdt_status);
  92. }
  93. }
  94. wdt_enable();
  95. }
  96. return len;
  97. }
  98. static struct watchdog_info ident = {
  99. .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
  100. WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  101. .identity = "Avila Watchdog",
  102. };
  103. static long avila_wdt_ioctl(struct file *file, unsigned int cmd,
  104. unsigned long arg)
  105. {
  106. int ret = -ENOTTY;
  107. int time;
  108. switch (cmd) {
  109. case WDIOC_GETSUPPORT:
  110. ret = copy_to_user((struct watchdog_info *)arg, &ident,
  111. sizeof(ident)) ? -EFAULT : 0;
  112. break;
  113. case WDIOC_GETSTATUS:
  114. ret = put_user(0, (int *)arg);
  115. break;
  116. case WDIOC_KEEPALIVE:
  117. wdt_enable();
  118. ret = 0;
  119. break;
  120. case WDIOC_SETTIMEOUT:
  121. ret = get_user(time, (int *)arg);
  122. if (ret)
  123. break;
  124. if (time <= 0 || time > 60) {
  125. ret = -EINVAL;
  126. break;
  127. }
  128. heartbeat = time;
  129. wdt_enable();
  130. /* Fall through */
  131. case WDIOC_GETTIMEOUT:
  132. ret = put_user(heartbeat, (int *)arg);
  133. break;
  134. }
  135. return ret;
  136. }
  137. static int avila_wdt_release(struct inode *inode, struct file *file)
  138. {
  139. if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
  140. wdt_disable();
  141. else
  142. printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
  143. "timer will not stop\n");
  144. clear_bit(WDT_IN_USE, &wdt_status);
  145. clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  146. return 0;
  147. }
  148. static const struct file_operations avila_wdt_fops = {
  149. .owner = THIS_MODULE,
  150. .llseek = no_llseek,
  151. .write = avila_wdt_write,
  152. .unlocked_ioctl = avila_wdt_ioctl,
  153. .open = avila_wdt_open,
  154. .release = avila_wdt_release,
  155. };
  156. static struct miscdevice avila_wdt_miscdev = {
  157. .minor = WATCHDOG_MINOR + 1,
  158. .name = "avila_watchdog",
  159. .fops = &avila_wdt_fops,
  160. };
  161. static int __init avila_wdt_init(void)
  162. {
  163. int ret;
  164. init_timer(&wdt_timer);
  165. wdt_timer.expires = 0;
  166. wdt_timer.data = 0;
  167. wdt_timer.function = wdt_refresh;
  168. ret = misc_register(&avila_wdt_miscdev);
  169. if (ret == 0)
  170. printk(KERN_INFO "Avila Watchdog Timer: heartbeat %d sec\n",
  171. heartbeat);
  172. return ret;
  173. }
  174. static void __exit avila_wdt_exit(void)
  175. {
  176. misc_deregister(&avila_wdt_miscdev);
  177. del_timer(&wdt_timer);
  178. wdt_disable();
  179. }
  180. module_init(avila_wdt_init);
  181. module_exit(avila_wdt_exit);
  182. MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
  183. MODULE_DESCRIPTION("Gateworks Avila Hardware Watchdog");
  184. module_param(heartbeat, int, 0);
  185. MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 20s)");
  186. module_param(nowayout, int, 0);
  187. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
  188. MODULE_LICENSE("GPL");
  189. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);