rdc321x_wdt.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * RDC321x watchdog driver
  4. *
  5. * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
  6. *
  7. * This driver is highly inspired from the cpu5_wdt driver
  8. */
  9. #include <linux/module.h>
  10. #include <linux/moduleparam.h>
  11. #include <linux/types.h>
  12. #include <linux/errno.h>
  13. #include <linux/miscdevice.h>
  14. #include <linux/fs.h>
  15. #include <linux/ioport.h>
  16. #include <linux/timer.h>
  17. #include <linux/completion.h>
  18. #include <linux/jiffies.h>
  19. #include <linux/platform_device.h>
  20. #include <linux/watchdog.h>
  21. #include <linux/io.h>
  22. #include <linux/uaccess.h>
  23. #include <linux/mfd/rdc321x.h>
  24. #define RDC_WDT_MASK 0x80000000 /* Mask */
  25. #define RDC_WDT_EN 0x00800000 /* Enable bit */
  26. #define RDC_WDT_WTI 0x00200000 /* Generate CPU reset/NMI/WDT on timeout */
  27. #define RDC_WDT_RST 0x00100000 /* Reset bit */
  28. #define RDC_WDT_WIF 0x00040000 /* WDT IRQ Flag */
  29. #define RDC_WDT_IRT 0x00000100 /* IRQ Routing table */
  30. #define RDC_WDT_CNT 0x00000001 /* WDT count */
  31. #define RDC_CLS_TMR 0x80003844 /* Clear timer */
  32. #define RDC_WDT_INTERVAL (HZ/10+1)
  33. static int ticks = 1000;
  34. /* some device data */
  35. static struct {
  36. struct completion stop;
  37. int running;
  38. struct timer_list timer;
  39. int queue;
  40. int default_ticks;
  41. unsigned long inuse;
  42. spinlock_t lock;
  43. struct pci_dev *sb_pdev;
  44. int base_reg;
  45. } rdc321x_wdt_device;
  46. /* generic helper functions */
  47. static void rdc321x_wdt_trigger(struct timer_list *unused)
  48. {
  49. unsigned long flags;
  50. u32 val;
  51. if (rdc321x_wdt_device.running)
  52. ticks--;
  53. /* keep watchdog alive */
  54. spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
  55. pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
  56. rdc321x_wdt_device.base_reg, &val);
  57. val |= RDC_WDT_EN;
  58. pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
  59. rdc321x_wdt_device.base_reg, val);
  60. spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
  61. /* requeue?? */
  62. if (rdc321x_wdt_device.queue && ticks)
  63. mod_timer(&rdc321x_wdt_device.timer,
  64. jiffies + RDC_WDT_INTERVAL);
  65. else {
  66. /* ticks doesn't matter anyway */
  67. complete(&rdc321x_wdt_device.stop);
  68. }
  69. }
  70. static void rdc321x_wdt_reset(void)
  71. {
  72. ticks = rdc321x_wdt_device.default_ticks;
  73. }
  74. static void rdc321x_wdt_start(void)
  75. {
  76. unsigned long flags;
  77. if (!rdc321x_wdt_device.queue) {
  78. rdc321x_wdt_device.queue = 1;
  79. /* Clear the timer */
  80. spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
  81. pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
  82. rdc321x_wdt_device.base_reg, RDC_CLS_TMR);
  83. /* Enable watchdog and set the timeout to 81.92 us */
  84. pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
  85. rdc321x_wdt_device.base_reg,
  86. RDC_WDT_EN | RDC_WDT_CNT);
  87. spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
  88. mod_timer(&rdc321x_wdt_device.timer,
  89. jiffies + RDC_WDT_INTERVAL);
  90. }
  91. /* if process dies, counter is not decremented */
  92. rdc321x_wdt_device.running++;
  93. }
  94. static int rdc321x_wdt_stop(void)
  95. {
  96. if (rdc321x_wdt_device.running)
  97. rdc321x_wdt_device.running = 0;
  98. ticks = rdc321x_wdt_device.default_ticks;
  99. return -EIO;
  100. }
  101. /* filesystem operations */
  102. static int rdc321x_wdt_open(struct inode *inode, struct file *file)
  103. {
  104. if (test_and_set_bit(0, &rdc321x_wdt_device.inuse))
  105. return -EBUSY;
  106. return stream_open(inode, file);
  107. }
  108. static int rdc321x_wdt_release(struct inode *inode, struct file *file)
  109. {
  110. clear_bit(0, &rdc321x_wdt_device.inuse);
  111. return 0;
  112. }
  113. static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
  114. unsigned long arg)
  115. {
  116. void __user *argp = (void __user *)arg;
  117. u32 value;
  118. static const struct watchdog_info ident = {
  119. .options = WDIOF_CARDRESET,
  120. .identity = "RDC321x WDT",
  121. };
  122. unsigned long flags;
  123. switch (cmd) {
  124. case WDIOC_KEEPALIVE:
  125. rdc321x_wdt_reset();
  126. break;
  127. case WDIOC_GETSTATUS:
  128. /* Read the value from the DATA register */
  129. spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
  130. pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
  131. rdc321x_wdt_device.base_reg, &value);
  132. spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
  133. if (copy_to_user(argp, &value, sizeof(u32)))
  134. return -EFAULT;
  135. break;
  136. case WDIOC_GETSUPPORT:
  137. if (copy_to_user(argp, &ident, sizeof(ident)))
  138. return -EFAULT;
  139. break;
  140. case WDIOC_SETOPTIONS:
  141. if (copy_from_user(&value, argp, sizeof(int)))
  142. return -EFAULT;
  143. switch (value) {
  144. case WDIOS_ENABLECARD:
  145. rdc321x_wdt_start();
  146. break;
  147. case WDIOS_DISABLECARD:
  148. return rdc321x_wdt_stop();
  149. default:
  150. return -EINVAL;
  151. }
  152. break;
  153. default:
  154. return -ENOTTY;
  155. }
  156. return 0;
  157. }
  158. static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf,
  159. size_t count, loff_t *ppos)
  160. {
  161. if (!count)
  162. return -EIO;
  163. rdc321x_wdt_reset();
  164. return count;
  165. }
  166. static const struct file_operations rdc321x_wdt_fops = {
  167. .owner = THIS_MODULE,
  168. .llseek = no_llseek,
  169. .unlocked_ioctl = rdc321x_wdt_ioctl,
  170. .compat_ioctl = compat_ptr_ioctl,
  171. .open = rdc321x_wdt_open,
  172. .write = rdc321x_wdt_write,
  173. .release = rdc321x_wdt_release,
  174. };
  175. static struct miscdevice rdc321x_wdt_misc = {
  176. .minor = WATCHDOG_MINOR,
  177. .name = "watchdog",
  178. .fops = &rdc321x_wdt_fops,
  179. };
  180. static int rdc321x_wdt_probe(struct platform_device *pdev)
  181. {
  182. int err;
  183. struct resource *r;
  184. struct rdc321x_wdt_pdata *pdata;
  185. pdata = dev_get_platdata(&pdev->dev);
  186. if (!pdata) {
  187. dev_err(&pdev->dev, "no platform data supplied\n");
  188. return -ENODEV;
  189. }
  190. r = platform_get_resource_byname(pdev, IORESOURCE_IO, "wdt-reg");
  191. if (!r) {
  192. dev_err(&pdev->dev, "failed to get wdt-reg resource\n");
  193. return -ENODEV;
  194. }
  195. rdc321x_wdt_device.sb_pdev = pdata->sb_pdev;
  196. rdc321x_wdt_device.base_reg = r->start;
  197. rdc321x_wdt_device.queue = 0;
  198. rdc321x_wdt_device.default_ticks = ticks;
  199. err = misc_register(&rdc321x_wdt_misc);
  200. if (err < 0) {
  201. dev_err(&pdev->dev, "misc_register failed\n");
  202. return err;
  203. }
  204. spin_lock_init(&rdc321x_wdt_device.lock);
  205. /* Reset the watchdog */
  206. pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
  207. rdc321x_wdt_device.base_reg, RDC_WDT_RST);
  208. init_completion(&rdc321x_wdt_device.stop);
  209. clear_bit(0, &rdc321x_wdt_device.inuse);
  210. timer_setup(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0);
  211. dev_info(&pdev->dev, "watchdog init success\n");
  212. return 0;
  213. }
  214. static int rdc321x_wdt_remove(struct platform_device *pdev)
  215. {
  216. if (rdc321x_wdt_device.queue) {
  217. rdc321x_wdt_device.queue = 0;
  218. wait_for_completion(&rdc321x_wdt_device.stop);
  219. }
  220. misc_deregister(&rdc321x_wdt_misc);
  221. return 0;
  222. }
  223. static struct platform_driver rdc321x_wdt_driver = {
  224. .probe = rdc321x_wdt_probe,
  225. .remove = rdc321x_wdt_remove,
  226. .driver = {
  227. .name = "rdc321x-wdt",
  228. },
  229. };
  230. module_platform_driver(rdc321x_wdt_driver);
  231. MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
  232. MODULE_DESCRIPTION("RDC321x watchdog driver");
  233. MODULE_LICENSE("GPL");