mpc83xx_wdt.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * mpc83xx_wdt.c - MPC83xx watchdog userspace interface
  3. *
  4. * Authors: Dave Updegraff <dave@cray.org>
  5. * Kumar Gala <galak@kernel.crashing.org>
  6. * Attribution: from 83xx_wst: Florian Schirmer <jolt@tuxbox.org>
  7. * ..and from sc520_wdt
  8. *
  9. * Note: it appears that you can only actually ENABLE or DISABLE the thing
  10. * once after POR. Once enabled, you cannot disable, and vice versa.
  11. *
  12. * This program is free software; you can redistribute it and/or modify it
  13. * under the terms of the GNU General Public License as published by the
  14. * Free Software Foundation; either version 2 of the License, or (at your
  15. * option) any later version.
  16. */
  17. #include <linux/fs.h>
  18. #include <linux/init.h>
  19. #include <linux/kernel.h>
  20. #include <linux/miscdevice.h>
  21. #include <linux/platform_device.h>
  22. #include <linux/module.h>
  23. #include <linux/watchdog.h>
  24. #include <linux/io.h>
  25. #include <linux/uaccess.h>
  26. struct mpc83xx_wdt {
  27. __be32 res0;
  28. __be32 swcrr; /* System watchdog control register */
  29. #define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */
  30. #define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */
  31. #define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/
  32. #define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */
  33. __be32 swcnr; /* System watchdog count register */
  34. u8 res1[2];
  35. __be16 swsrr; /* System watchdog service register */
  36. u8 res2[0xF0];
  37. };
  38. static struct mpc83xx_wdt __iomem *wd_base;
  39. static u16 timeout = 0xffff;
  40. module_param(timeout, ushort, 0);
  41. MODULE_PARM_DESC(timeout,
  42. "Watchdog timeout in ticks. (0<timeout<65536, default=65535");
  43. static int reset = 1;
  44. module_param(reset, bool, 0);
  45. MODULE_PARM_DESC(reset,
  46. "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset");
  47. /*
  48. * We always prescale, but if someone really doesn't want to they can set this
  49. * to 0
  50. */
  51. static int prescale = 1;
  52. static unsigned int timeout_sec;
  53. static unsigned long wdt_is_open;
  54. static DEFINE_SPINLOCK(wdt_spinlock);
  55. static void mpc83xx_wdt_keepalive(void)
  56. {
  57. /* Ping the WDT */
  58. spin_lock(&wdt_spinlock);
  59. out_be16(&wd_base->swsrr, 0x556c);
  60. out_be16(&wd_base->swsrr, 0xaa39);
  61. spin_unlock(&wdt_spinlock);
  62. }
  63. static ssize_t mpc83xx_wdt_write(struct file *file, const char __user *buf,
  64. size_t count, loff_t *ppos)
  65. {
  66. if (count)
  67. mpc83xx_wdt_keepalive();
  68. return count;
  69. }
  70. static int mpc83xx_wdt_open(struct inode *inode, struct file *file)
  71. {
  72. u32 tmp = SWCRR_SWEN;
  73. if (test_and_set_bit(0, &wdt_is_open))
  74. return -EBUSY;
  75. /* Once we start the watchdog we can't stop it */
  76. __module_get(THIS_MODULE);
  77. /* Good, fire up the show */
  78. if (prescale)
  79. tmp |= SWCRR_SWPR;
  80. if (reset)
  81. tmp |= SWCRR_SWRI;
  82. tmp |= timeout << 16;
  83. out_be32(&wd_base->swcrr, tmp);
  84. return nonseekable_open(inode, file);
  85. }
  86. static int mpc83xx_wdt_release(struct inode *inode, struct file *file)
  87. {
  88. printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n");
  89. mpc83xx_wdt_keepalive();
  90. clear_bit(0, &wdt_is_open);
  91. return 0;
  92. }
  93. static long mpc83xx_wdt_ioctl(struct file *file, unsigned int cmd,
  94. unsigned long arg)
  95. {
  96. void __user *argp = (void __user *)arg;
  97. int __user *p = argp;
  98. static struct watchdog_info ident = {
  99. .options = WDIOF_KEEPALIVEPING,
  100. .firmware_version = 1,
  101. .identity = "MPC83xx",
  102. };
  103. switch (cmd) {
  104. case WDIOC_GETSUPPORT:
  105. return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
  106. case WDIOC_GETSTATUS:
  107. case WDIOC_GETBOOTSTATUS:
  108. return put_user(0, p);
  109. case WDIOC_KEEPALIVE:
  110. mpc83xx_wdt_keepalive();
  111. return 0;
  112. case WDIOC_GETTIMEOUT:
  113. return put_user(timeout_sec, p);
  114. default:
  115. return -ENOTTY;
  116. }
  117. }
  118. static const struct file_operations mpc83xx_wdt_fops = {
  119. .owner = THIS_MODULE,
  120. .llseek = no_llseek,
  121. .write = mpc83xx_wdt_write,
  122. .unlocked_ioctl = mpc83xx_wdt_ioctl,
  123. .open = mpc83xx_wdt_open,
  124. .release = mpc83xx_wdt_release,
  125. };
  126. static struct miscdevice mpc83xx_wdt_miscdev = {
  127. .minor = WATCHDOG_MINOR,
  128. .name = "watchdog",
  129. .fops = &mpc83xx_wdt_fops,
  130. };
  131. static int __devinit mpc83xx_wdt_probe(struct platform_device *dev)
  132. {
  133. struct resource *r;
  134. int ret;
  135. unsigned int *freq = dev->dev.platform_data;
  136. /* get a pointer to the register memory */
  137. r = platform_get_resource(dev, IORESOURCE_MEM, 0);
  138. if (!r) {
  139. ret = -ENODEV;
  140. goto err_out;
  141. }
  142. wd_base = ioremap(r->start, sizeof(struct mpc83xx_wdt));
  143. if (wd_base == NULL) {
  144. ret = -ENOMEM;
  145. goto err_out;
  146. }
  147. ret = misc_register(&mpc83xx_wdt_miscdev);
  148. if (ret) {
  149. printk(KERN_ERR "cannot register miscdev on minor=%d "
  150. "(err=%d)\n",
  151. WATCHDOG_MINOR, ret);
  152. goto err_unmap;
  153. }
  154. /* Calculate the timeout in seconds */
  155. if (prescale)
  156. timeout_sec = (timeout * 0x10000) / (*freq);
  157. else
  158. timeout_sec = timeout / (*freq);
  159. printk(KERN_INFO "WDT driver for MPC83xx initialized. "
  160. "mode:%s timeout=%d (%d seconds)\n",
  161. reset ? "reset":"interrupt", timeout, timeout_sec);
  162. return 0;
  163. err_unmap:
  164. iounmap(wd_base);
  165. err_out:
  166. return ret;
  167. }
  168. static int __devexit mpc83xx_wdt_remove(struct platform_device *dev)
  169. {
  170. misc_deregister(&mpc83xx_wdt_miscdev);
  171. iounmap(wd_base);
  172. return 0;
  173. }
  174. static struct platform_driver mpc83xx_wdt_driver = {
  175. .probe = mpc83xx_wdt_probe,
  176. .remove = __devexit_p(mpc83xx_wdt_remove),
  177. .driver = {
  178. .name = "mpc83xx_wdt",
  179. .owner = THIS_MODULE,
  180. },
  181. };
  182. static int __init mpc83xx_wdt_init(void)
  183. {
  184. return platform_driver_register(&mpc83xx_wdt_driver);
  185. }
  186. static void __exit mpc83xx_wdt_exit(void)
  187. {
  188. platform_driver_unregister(&mpc83xx_wdt_driver);
  189. }
  190. module_init(mpc83xx_wdt_init);
  191. module_exit(mpc83xx_wdt_exit);
  192. MODULE_AUTHOR("Dave Updegraff, Kumar Gala");
  193. MODULE_DESCRIPTION("Driver for watchdog timer in MPC83xx uProcessor");
  194. MODULE_LICENSE("GPL");
  195. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
  196. MODULE_ALIAS("platform:mpc83xx_wdt");