dw_wdt.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /*
  2. * Copyright 2010-2011 Picochip Ltd., Jamie Iles
  3. * http://www.picochip.com
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License
  7. * as published by the Free Software Foundation; either version
  8. * 2 of the License, or (at your option) any later version.
  9. *
  10. * This file implements a driver for the Synopsys DesignWare watchdog device
  11. * in the many subsystems. The watchdog has 16 different timeout periods
  12. * and these are a function of the input clock frequency.
  13. *
  14. * The DesignWare watchdog cannot be stopped once it has been started so we
  15. * use a software timer to implement a ping that will keep the watchdog alive.
  16. * If we receive an expected close for the watchdog then we keep the timer
  17. * running, otherwise the timer is stopped and the watchdog will expire.
  18. */
  19. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  20. #include <linux/bitops.h>
  21. #include <linux/clk.h>
  22. #include <linux/device.h>
  23. #include <linux/err.h>
  24. #include <linux/fs.h>
  25. #include <linux/io.h>
  26. #include <linux/kernel.h>
  27. #include <linux/miscdevice.h>
  28. #include <linux/module.h>
  29. #include <linux/moduleparam.h>
  30. #include <linux/of.h>
  31. #include <linux/pm.h>
  32. #include <linux/platform_device.h>
  33. #include <linux/spinlock.h>
  34. #include <linux/timer.h>
  35. #include <linux/uaccess.h>
  36. #include <linux/watchdog.h>
  37. #define WDOG_CONTROL_REG_OFFSET 0x00
  38. #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01
  39. #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04
  40. #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08
  41. #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c
  42. #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76
  43. /* The maximum TOP (timeout period) value that can be set in the watchdog. */
  44. #define DW_WDT_MAX_TOP 15
  45. static bool nowayout = WATCHDOG_NOWAYOUT;
  46. module_param(nowayout, bool, 0);
  47. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
  48. "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  49. #define WDT_TIMEOUT (HZ / 2)
  50. static struct {
  51. spinlock_t lock;
  52. void __iomem *regs;
  53. struct clk *clk;
  54. unsigned long in_use;
  55. unsigned long next_heartbeat;
  56. struct timer_list timer;
  57. int expect_close;
  58. } dw_wdt;
  59. static inline int dw_wdt_is_enabled(void)
  60. {
  61. return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) &
  62. WDOG_CONTROL_REG_WDT_EN_MASK;
  63. }
  64. static inline int dw_wdt_top_in_seconds(unsigned top)
  65. {
  66. /*
  67. * There are 16 possible timeout values in 0..15 where the number of
  68. * cycles is 2 ^ (16 + i) and the watchdog counts down.
  69. */
  70. return (1 << (16 + top)) / clk_get_rate(dw_wdt.clk);
  71. }
  72. static int dw_wdt_get_top(void)
  73. {
  74. int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
  75. return dw_wdt_top_in_seconds(top);
  76. }
  77. static inline void dw_wdt_set_next_heartbeat(void)
  78. {
  79. dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ;
  80. }
  81. static int dw_wdt_set_top(unsigned top_s)
  82. {
  83. int i, top_val = DW_WDT_MAX_TOP;
  84. /*
  85. * Iterate over the timeout values until we find the closest match. We
  86. * always look for >=.
  87. */
  88. for (i = 0; i <= DW_WDT_MAX_TOP; ++i)
  89. if (dw_wdt_top_in_seconds(i) >= top_s) {
  90. top_val = i;
  91. break;
  92. }
  93. /* Set the new value in the watchdog. */
  94. writel(top_val, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
  95. dw_wdt_set_next_heartbeat();
  96. return dw_wdt_top_in_seconds(top_val);
  97. }
  98. static void dw_wdt_keepalive(void)
  99. {
  100. writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs +
  101. WDOG_COUNTER_RESTART_REG_OFFSET);
  102. }
  103. static void dw_wdt_ping(unsigned long data)
  104. {
  105. if (time_before(jiffies, dw_wdt.next_heartbeat) ||
  106. (!nowayout && !dw_wdt.in_use)) {
  107. dw_wdt_keepalive();
  108. mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
  109. } else
  110. pr_crit("keepalive missed, machine will reset\n");
  111. }
  112. static int dw_wdt_open(struct inode *inode, struct file *filp)
  113. {
  114. if (test_and_set_bit(0, &dw_wdt.in_use))
  115. return -EBUSY;
  116. /* Make sure we don't get unloaded. */
  117. __module_get(THIS_MODULE);
  118. spin_lock(&dw_wdt.lock);
  119. if (!dw_wdt_is_enabled()) {
  120. /*
  121. * The watchdog is not currently enabled. Set the timeout to
  122. * the maximum and then start it.
  123. */
  124. dw_wdt_set_top(DW_WDT_MAX_TOP);
  125. writel(WDOG_CONTROL_REG_WDT_EN_MASK,
  126. dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
  127. }
  128. dw_wdt_set_next_heartbeat();
  129. spin_unlock(&dw_wdt.lock);
  130. return nonseekable_open(inode, filp);
  131. }
  132. static ssize_t dw_wdt_write(struct file *filp, const char __user *buf,
  133. size_t len, loff_t *offset)
  134. {
  135. if (!len)
  136. return 0;
  137. if (!nowayout) {
  138. size_t i;
  139. dw_wdt.expect_close = 0;
  140. for (i = 0; i < len; ++i) {
  141. char c;
  142. if (get_user(c, buf + i))
  143. return -EFAULT;
  144. if (c == 'V') {
  145. dw_wdt.expect_close = 1;
  146. break;
  147. }
  148. }
  149. }
  150. dw_wdt_set_next_heartbeat();
  151. mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
  152. return len;
  153. }
  154. static u32 dw_wdt_time_left(void)
  155. {
  156. return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
  157. clk_get_rate(dw_wdt.clk);
  158. }
  159. static const struct watchdog_info dw_wdt_ident = {
  160. .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
  161. WDIOF_MAGICCLOSE,
  162. .identity = "Synopsys DesignWare Watchdog",
  163. };
  164. static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  165. {
  166. unsigned long val;
  167. int timeout;
  168. switch (cmd) {
  169. case WDIOC_GETSUPPORT:
  170. return copy_to_user((void __user *)arg, &dw_wdt_ident,
  171. sizeof(dw_wdt_ident)) ? -EFAULT : 0;
  172. case WDIOC_GETSTATUS:
  173. case WDIOC_GETBOOTSTATUS:
  174. return put_user(0, (int __user *)arg);
  175. case WDIOC_KEEPALIVE:
  176. dw_wdt_set_next_heartbeat();
  177. return 0;
  178. case WDIOC_SETTIMEOUT:
  179. if (get_user(val, (int __user *)arg))
  180. return -EFAULT;
  181. timeout = dw_wdt_set_top(val);
  182. return put_user(timeout , (int __user *)arg);
  183. case WDIOC_GETTIMEOUT:
  184. return put_user(dw_wdt_get_top(), (int __user *)arg);
  185. case WDIOC_GETTIMELEFT:
  186. /* Get the time left until expiry. */
  187. if (get_user(val, (int __user *)arg))
  188. return -EFAULT;
  189. return put_user(dw_wdt_time_left(), (int __user *)arg);
  190. default:
  191. return -ENOTTY;
  192. }
  193. }
  194. static int dw_wdt_release(struct inode *inode, struct file *filp)
  195. {
  196. clear_bit(0, &dw_wdt.in_use);
  197. if (!dw_wdt.expect_close) {
  198. del_timer(&dw_wdt.timer);
  199. if (!nowayout)
  200. pr_crit("unexpected close, system will reboot soon\n");
  201. else
  202. pr_crit("watchdog cannot be disabled, system will reboot soon\n");
  203. }
  204. dw_wdt.expect_close = 0;
  205. return 0;
  206. }
  207. #ifdef CONFIG_PM_SLEEP
  208. static int dw_wdt_suspend(struct device *dev)
  209. {
  210. clk_disable_unprepare(dw_wdt.clk);
  211. return 0;
  212. }
  213. static int dw_wdt_resume(struct device *dev)
  214. {
  215. int err = clk_prepare_enable(dw_wdt.clk);
  216. if (err)
  217. return err;
  218. dw_wdt_keepalive();
  219. return 0;
  220. }
  221. #endif /* CONFIG_PM_SLEEP */
  222. static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume);
  223. static const struct file_operations wdt_fops = {
  224. .owner = THIS_MODULE,
  225. .llseek = no_llseek,
  226. .open = dw_wdt_open,
  227. .write = dw_wdt_write,
  228. .unlocked_ioctl = dw_wdt_ioctl,
  229. .release = dw_wdt_release
  230. };
  231. static struct miscdevice dw_wdt_miscdev = {
  232. .fops = &wdt_fops,
  233. .name = "watchdog",
  234. .minor = WATCHDOG_MINOR,
  235. };
  236. static int dw_wdt_drv_probe(struct platform_device *pdev)
  237. {
  238. int ret;
  239. struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  240. if (!mem)
  241. return -EINVAL;
  242. dw_wdt.regs = devm_ioremap_resource(&pdev->dev, mem);
  243. if (IS_ERR(dw_wdt.regs))
  244. return PTR_ERR(dw_wdt.regs);
  245. dw_wdt.clk = devm_clk_get(&pdev->dev, NULL);
  246. if (IS_ERR(dw_wdt.clk))
  247. return PTR_ERR(dw_wdt.clk);
  248. ret = clk_prepare_enable(dw_wdt.clk);
  249. if (ret)
  250. return ret;
  251. spin_lock_init(&dw_wdt.lock);
  252. ret = misc_register(&dw_wdt_miscdev);
  253. if (ret)
  254. goto out_disable_clk;
  255. dw_wdt_set_next_heartbeat();
  256. setup_timer(&dw_wdt.timer, dw_wdt_ping, 0);
  257. mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
  258. return 0;
  259. out_disable_clk:
  260. clk_disable_unprepare(dw_wdt.clk);
  261. return ret;
  262. }
  263. static int dw_wdt_drv_remove(struct platform_device *pdev)
  264. {
  265. misc_deregister(&dw_wdt_miscdev);
  266. clk_disable_unprepare(dw_wdt.clk);
  267. return 0;
  268. }
  269. #ifdef CONFIG_OF
  270. static const struct of_device_id dw_wdt_of_match[] = {
  271. { .compatible = "snps,dw-wdt", },
  272. { /* sentinel */ }
  273. };
  274. MODULE_DEVICE_TABLE(of, dw_wdt_of_match);
  275. #endif
  276. static struct platform_driver dw_wdt_driver = {
  277. .probe = dw_wdt_drv_probe,
  278. .remove = dw_wdt_drv_remove,
  279. .driver = {
  280. .name = "dw_wdt",
  281. .owner = THIS_MODULE,
  282. .of_match_table = of_match_ptr(dw_wdt_of_match),
  283. .pm = &dw_wdt_pm_ops,
  284. },
  285. };
  286. module_platform_driver(dw_wdt_driver);
  287. MODULE_AUTHOR("Jamie Iles");
  288. MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver");
  289. MODULE_LICENSE("GPL");