at32ap700x_wdt.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. * Watchdog driver for Atmel AT32AP700X devices
  3. *
  4. * Copyright (C) 2005-2006 Atmel Corporation
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. */
  10. #include <linux/init.h>
  11. #include <linux/kernel.h>
  12. #include <linux/module.h>
  13. #include <linux/moduleparam.h>
  14. #include <linux/miscdevice.h>
  15. #include <linux/fs.h>
  16. #include <linux/platform_device.h>
  17. #include <linux/watchdog.h>
  18. #include <asm/uaccess.h>
  19. #include <asm/io.h>
  20. #define TIMEOUT_MIN 1
  21. #define TIMEOUT_DEFAULT CONFIG_AT32AP700X_WDT_TIMEOUT
  22. #define TIMEOUT_MAX 2
  23. /* Watchdog registers and write/read macro */
  24. #define WDT_CTRL 0x00
  25. #define WDT_CTRL_EN 0
  26. #define WDT_CTRL_PSEL 8
  27. #define WDT_CTRL_KEY 24
  28. #define WDT_CLR 0x04
  29. #define WDT_BIT(name) (1 << WDT_##name)
  30. #define WDT_BF(name,value) ((value) << WDT_##name)
  31. #define wdt_readl(dev,reg) \
  32. __raw_readl((dev)->regs + WDT_##reg)
  33. #define wdt_writel(dev,reg,value) \
  34. __raw_writel((value), (dev)->regs + WDT_##reg)
  35. struct wdt_at32ap700x {
  36. void __iomem *regs;
  37. int timeout;
  38. int users;
  39. struct miscdevice miscdev;
  40. };
  41. static struct wdt_at32ap700x *wdt;
  42. /*
  43. * Disable the watchdog.
  44. */
  45. static void inline at32_wdt_stop(void)
  46. {
  47. unsigned long psel = wdt_readl(wdt, CTRL) & WDT_BF(CTRL_PSEL, 0x0f);
  48. wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0x55));
  49. wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0xaa));
  50. }
  51. /*
  52. * Enable and reset the watchdog.
  53. */
  54. static void inline at32_wdt_start(void)
  55. {
  56. /* 0xf is 2^16 divider = 2 sec, 0xe is 2^15 divider = 1 sec */
  57. unsigned long psel = (wdt->timeout > 1) ? 0xf : 0xe;
  58. wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN)
  59. | WDT_BF(CTRL_PSEL, psel)
  60. | WDT_BF(CTRL_KEY, 0x55));
  61. wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN)
  62. | WDT_BF(CTRL_PSEL, psel)
  63. | WDT_BF(CTRL_KEY, 0xaa));
  64. }
  65. /*
  66. * Pat the watchdog timer.
  67. */
  68. static void inline at32_wdt_pat(void)
  69. {
  70. wdt_writel(wdt, CLR, 0x42);
  71. }
  72. /*
  73. * Watchdog device is opened, and watchdog starts running.
  74. */
  75. static int at32_wdt_open(struct inode *inode, struct file *file)
  76. {
  77. if (test_and_set_bit(1, &wdt->users))
  78. return -EBUSY;
  79. at32_wdt_start();
  80. return nonseekable_open(inode, file);
  81. }
  82. /*
  83. * Close the watchdog device. If CONFIG_WATCHDOG_NOWAYOUT is _not_ defined then
  84. * the watchdog is also disabled.
  85. */
  86. static int at32_wdt_close(struct inode *inode, struct file *file)
  87. {
  88. #ifndef CONFIG_WATCHDOG_NOWAYOUT
  89. at32_wdt_stop();
  90. #endif
  91. clear_bit(1, &wdt->users);
  92. return 0;
  93. }
  94. /*
  95. * Change the watchdog time interval.
  96. */
  97. static int at32_wdt_settimeout(int time)
  98. {
  99. /*
  100. * All counting occurs at 1 / SLOW_CLOCK (32 kHz) and max prescaler is
  101. * 2 ^ 16 allowing up to 2 seconds timeout.
  102. */
  103. if ((time < TIMEOUT_MIN) || (time > TIMEOUT_MAX))
  104. return -EINVAL;
  105. /* Set new watchdog time. It will be used when at32_wdt_start() is called. */
  106. wdt->timeout = time;
  107. return 0;
  108. }
  109. static struct watchdog_info at32_wdt_info = {
  110. .identity = "at32ap700x watchdog",
  111. .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  112. };
  113. /*
  114. * Handle commands from user-space.
  115. */
  116. static int at32_wdt_ioctl(struct inode *inode, struct file *file,
  117. unsigned int cmd, unsigned long arg)
  118. {
  119. int ret = -ENOTTY;
  120. int time;
  121. void __user *argp = (void __user *)arg;
  122. int __user *p = argp;
  123. switch(cmd) {
  124. case WDIOC_KEEPALIVE:
  125. at32_wdt_pat();
  126. ret = 0;
  127. break;
  128. case WDIOC_GETSUPPORT:
  129. ret = copy_to_user(argp, &at32_wdt_info,
  130. sizeof(at32_wdt_info)) ? -EFAULT : 0;
  131. break;
  132. case WDIOC_SETTIMEOUT:
  133. ret = get_user(time, p);
  134. if (ret)
  135. break;
  136. ret = at32_wdt_settimeout(time);
  137. if (ret)
  138. break;
  139. /* Enable new time value */
  140. at32_wdt_start();
  141. /* fall through */
  142. case WDIOC_GETTIMEOUT:
  143. ret = put_user(wdt->timeout, p);
  144. break;
  145. case WDIOC_GETSTATUS: /* fall through */
  146. case WDIOC_GETBOOTSTATUS:
  147. ret = put_user(0, p);
  148. break;
  149. case WDIOC_SETOPTIONS:
  150. ret = get_user(time, p);
  151. if (ret)
  152. break;
  153. if (time & WDIOS_DISABLECARD)
  154. at32_wdt_stop();
  155. if (time & WDIOS_ENABLECARD)
  156. at32_wdt_start();
  157. ret = 0;
  158. break;
  159. }
  160. return ret;
  161. }
  162. static ssize_t at32_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
  163. {
  164. at32_wdt_pat();
  165. return len;
  166. }
  167. static const struct file_operations at32_wdt_fops = {
  168. .owner = THIS_MODULE,
  169. .llseek = no_llseek,
  170. .ioctl = at32_wdt_ioctl,
  171. .open = at32_wdt_open,
  172. .release = at32_wdt_close,
  173. .write = at32_wdt_write,
  174. };
  175. static int __init at32_wdt_probe(struct platform_device *pdev)
  176. {
  177. struct resource *regs;
  178. int ret;
  179. if (wdt) {
  180. dev_dbg(&pdev->dev, "only 1 wdt instance supported.\n");
  181. return -EBUSY;
  182. }
  183. regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  184. if (!regs) {
  185. dev_dbg(&pdev->dev, "missing mmio resource\n");
  186. return -ENXIO;
  187. }
  188. wdt = kzalloc(sizeof(struct wdt_at32ap700x), GFP_KERNEL);
  189. if (!wdt) {
  190. dev_dbg(&pdev->dev, "no memory for wdt structure\n");
  191. return -ENOMEM;
  192. }
  193. wdt->regs = ioremap(regs->start, regs->end - regs->start + 1);
  194. wdt->users = 0;
  195. wdt->miscdev.minor = WATCHDOG_MINOR;
  196. wdt->miscdev.name = "watchdog";
  197. wdt->miscdev.fops = &at32_wdt_fops;
  198. if (at32_wdt_settimeout(TIMEOUT_DEFAULT)) {
  199. at32_wdt_settimeout(TIMEOUT_MAX);
  200. dev_dbg(&pdev->dev,
  201. "default timeout invalid, set to %d sec.\n",
  202. TIMEOUT_MAX);
  203. }
  204. ret = misc_register(&wdt->miscdev);
  205. if (ret) {
  206. dev_dbg(&pdev->dev, "failed to register wdt miscdev\n");
  207. goto err_register;
  208. }
  209. platform_set_drvdata(pdev, wdt);
  210. wdt->miscdev.parent = &pdev->dev;
  211. dev_info(&pdev->dev, "AT32AP700X WDT at 0x%p\n", wdt->regs);
  212. return 0;
  213. err_register:
  214. kfree(wdt);
  215. wdt = NULL;
  216. return ret;
  217. }
  218. static int __exit at32_wdt_remove(struct platform_device *pdev)
  219. {
  220. if (wdt && platform_get_drvdata(pdev) == wdt) {
  221. misc_deregister(&wdt->miscdev);
  222. kfree(wdt);
  223. wdt = NULL;
  224. platform_set_drvdata(pdev, NULL);
  225. }
  226. return 0;
  227. }
  228. static void at32_wdt_shutdown(struct platform_device *pdev)
  229. {
  230. at32_wdt_stop();
  231. }
  232. #ifdef CONFIG_PM
  233. static int at32_wdt_suspend(struct platform_device *pdev, pm_message_t message)
  234. {
  235. at32_wdt_stop();
  236. return 0;
  237. }
  238. static int at32_wdt_resume(struct platform_device *pdev)
  239. {
  240. if (wdt->users)
  241. at32_wdt_start();
  242. return 0;
  243. }
  244. #endif
  245. static struct platform_driver at32_wdt_driver = {
  246. .remove = __exit_p(at32_wdt_remove),
  247. #ifdef CONFIG_PM
  248. .suspend = at32_wdt_suspend,
  249. .resume = at32_wdt_resume,
  250. #endif
  251. .driver = {
  252. .name = "at32_wdt",
  253. .owner = THIS_MODULE,
  254. },
  255. .shutdown = at32_wdt_shutdown,
  256. };
  257. static int __init at32_wdt_init(void)
  258. {
  259. return platform_driver_probe(&at32_wdt_driver, at32_wdt_probe);
  260. }
  261. module_init(at32_wdt_init);
  262. static void __exit at32_wdt_exit(void)
  263. {
  264. platform_driver_unregister(&at32_wdt_driver);
  265. }
  266. module_exit(at32_wdt_exit);
  267. MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
  268. MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X");
  269. MODULE_LICENSE("GPL");
  270. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);