ar7_wdt.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * drivers/watchdog/ar7_wdt.c
  4. *
  5. * Copyright (C) 2007 Nicolas Thill <nico@openwrt.org>
  6. * Copyright (c) 2005 Enrik Berkhan <Enrik.Berkhan@akk.org>
  7. *
  8. * Some code taken from:
  9. * National Semiconductor SCx200 Watchdog support
  10. * Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
  11. *
  12. */
  13. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14. #include <linux/module.h>
  15. #include <linux/moduleparam.h>
  16. #include <linux/errno.h>
  17. #include <linux/miscdevice.h>
  18. #include <linux/platform_device.h>
  19. #include <linux/watchdog.h>
  20. #include <linux/fs.h>
  21. #include <linux/ioport.h>
  22. #include <linux/io.h>
  23. #include <linux/uaccess.h>
  24. #include <linux/clk.h>
  25. #include <asm/addrspace.h>
  26. #include <asm/mach-ar7/ar7.h>
  27. #define LONGNAME "TI AR7 Watchdog Timer"
  28. MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
  29. MODULE_DESCRIPTION(LONGNAME);
  30. MODULE_LICENSE("GPL");
  31. static int margin = 60;
  32. module_param(margin, int, 0);
  33. MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
  34. static bool nowayout = WATCHDOG_NOWAYOUT;
  35. module_param(nowayout, bool, 0);
  36. MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
  37. #define READ_REG(x) readl((void __iomem *)&(x))
  38. #define WRITE_REG(x, v) writel((v), (void __iomem *)&(x))
  39. struct ar7_wdt {
  40. u32 kick_lock;
  41. u32 kick;
  42. u32 change_lock;
  43. u32 change;
  44. u32 disable_lock;
  45. u32 disable;
  46. u32 prescale_lock;
  47. u32 prescale;
  48. };
  49. static unsigned long wdt_is_open;
  50. static unsigned expect_close;
  51. static DEFINE_SPINLOCK(wdt_lock);
  52. /* XXX currently fixed, allows max margin ~68.72 secs */
  53. #define prescale_value 0xffff
  54. /* Resource of the WDT registers */
  55. static struct resource *ar7_regs_wdt;
  56. /* Pointer to the remapped WDT IO space */
  57. static struct ar7_wdt *ar7_wdt;
  58. static struct clk *vbus_clk;
  59. static void ar7_wdt_kick(u32 value)
  60. {
  61. WRITE_REG(ar7_wdt->kick_lock, 0x5555);
  62. if ((READ_REG(ar7_wdt->kick_lock) & 3) == 1) {
  63. WRITE_REG(ar7_wdt->kick_lock, 0xaaaa);
  64. if ((READ_REG(ar7_wdt->kick_lock) & 3) == 3) {
  65. WRITE_REG(ar7_wdt->kick, value);
  66. return;
  67. }
  68. }
  69. pr_err("failed to unlock WDT kick reg\n");
  70. }
  71. static void ar7_wdt_prescale(u32 value)
  72. {
  73. WRITE_REG(ar7_wdt->prescale_lock, 0x5a5a);
  74. if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 1) {
  75. WRITE_REG(ar7_wdt->prescale_lock, 0xa5a5);
  76. if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 3) {
  77. WRITE_REG(ar7_wdt->prescale, value);
  78. return;
  79. }
  80. }
  81. pr_err("failed to unlock WDT prescale reg\n");
  82. }
  83. static void ar7_wdt_change(u32 value)
  84. {
  85. WRITE_REG(ar7_wdt->change_lock, 0x6666);
  86. if ((READ_REG(ar7_wdt->change_lock) & 3) == 1) {
  87. WRITE_REG(ar7_wdt->change_lock, 0xbbbb);
  88. if ((READ_REG(ar7_wdt->change_lock) & 3) == 3) {
  89. WRITE_REG(ar7_wdt->change, value);
  90. return;
  91. }
  92. }
  93. pr_err("failed to unlock WDT change reg\n");
  94. }
  95. static void ar7_wdt_disable(u32 value)
  96. {
  97. WRITE_REG(ar7_wdt->disable_lock, 0x7777);
  98. if ((READ_REG(ar7_wdt->disable_lock) & 3) == 1) {
  99. WRITE_REG(ar7_wdt->disable_lock, 0xcccc);
  100. if ((READ_REG(ar7_wdt->disable_lock) & 3) == 2) {
  101. WRITE_REG(ar7_wdt->disable_lock, 0xdddd);
  102. if ((READ_REG(ar7_wdt->disable_lock) & 3) == 3) {
  103. WRITE_REG(ar7_wdt->disable, value);
  104. return;
  105. }
  106. }
  107. }
  108. pr_err("failed to unlock WDT disable reg\n");
  109. }
  110. static void ar7_wdt_update_margin(int new_margin)
  111. {
  112. u32 change;
  113. u32 vbus_rate;
  114. vbus_rate = clk_get_rate(vbus_clk);
  115. change = new_margin * (vbus_rate / prescale_value);
  116. if (change < 1)
  117. change = 1;
  118. if (change > 0xffff)
  119. change = 0xffff;
  120. ar7_wdt_change(change);
  121. margin = change * prescale_value / vbus_rate;
  122. pr_info("timer margin %d seconds (prescale %d, change %d, freq %d)\n",
  123. margin, prescale_value, change, vbus_rate);
  124. }
  125. static void ar7_wdt_enable_wdt(void)
  126. {
  127. pr_debug("enabling watchdog timer\n");
  128. ar7_wdt_disable(1);
  129. ar7_wdt_kick(1);
  130. }
  131. static void ar7_wdt_disable_wdt(void)
  132. {
  133. pr_debug("disabling watchdog timer\n");
  134. ar7_wdt_disable(0);
  135. }
  136. static int ar7_wdt_open(struct inode *inode, struct file *file)
  137. {
  138. /* only allow one at a time */
  139. if (test_and_set_bit(0, &wdt_is_open))
  140. return -EBUSY;
  141. ar7_wdt_enable_wdt();
  142. expect_close = 0;
  143. return stream_open(inode, file);
  144. }
  145. static int ar7_wdt_release(struct inode *inode, struct file *file)
  146. {
  147. if (!expect_close)
  148. pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n");
  149. else if (!nowayout)
  150. ar7_wdt_disable_wdt();
  151. clear_bit(0, &wdt_is_open);
  152. return 0;
  153. }
  154. static ssize_t ar7_wdt_write(struct file *file, const char *data,
  155. size_t len, loff_t *ppos)
  156. {
  157. /* check for a magic close character */
  158. if (len) {
  159. size_t i;
  160. spin_lock(&wdt_lock);
  161. ar7_wdt_kick(1);
  162. spin_unlock(&wdt_lock);
  163. expect_close = 0;
  164. for (i = 0; i < len; ++i) {
  165. char c;
  166. if (get_user(c, data + i))
  167. return -EFAULT;
  168. if (c == 'V')
  169. expect_close = 1;
  170. }
  171. }
  172. return len;
  173. }
  174. static long ar7_wdt_ioctl(struct file *file,
  175. unsigned int cmd, unsigned long arg)
  176. {
  177. static const struct watchdog_info ident = {
  178. .identity = LONGNAME,
  179. .firmware_version = 1,
  180. .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
  181. WDIOF_MAGICCLOSE),
  182. };
  183. int new_margin;
  184. switch (cmd) {
  185. case WDIOC_GETSUPPORT:
  186. if (copy_to_user((struct watchdog_info *)arg, &ident,
  187. sizeof(ident)))
  188. return -EFAULT;
  189. return 0;
  190. case WDIOC_GETSTATUS:
  191. case WDIOC_GETBOOTSTATUS:
  192. if (put_user(0, (int *)arg))
  193. return -EFAULT;
  194. return 0;
  195. case WDIOC_KEEPALIVE:
  196. ar7_wdt_kick(1);
  197. return 0;
  198. case WDIOC_SETTIMEOUT:
  199. if (get_user(new_margin, (int *)arg))
  200. return -EFAULT;
  201. if (new_margin < 1)
  202. return -EINVAL;
  203. spin_lock(&wdt_lock);
  204. ar7_wdt_update_margin(new_margin);
  205. ar7_wdt_kick(1);
  206. spin_unlock(&wdt_lock);
  207. fallthrough;
  208. case WDIOC_GETTIMEOUT:
  209. if (put_user(margin, (int *)arg))
  210. return -EFAULT;
  211. return 0;
  212. default:
  213. return -ENOTTY;
  214. }
  215. }
  216. static const struct file_operations ar7_wdt_fops = {
  217. .owner = THIS_MODULE,
  218. .write = ar7_wdt_write,
  219. .unlocked_ioctl = ar7_wdt_ioctl,
  220. .compat_ioctl = compat_ptr_ioctl,
  221. .open = ar7_wdt_open,
  222. .release = ar7_wdt_release,
  223. .llseek = no_llseek,
  224. };
  225. static struct miscdevice ar7_wdt_miscdev = {
  226. .minor = WATCHDOG_MINOR,
  227. .name = "watchdog",
  228. .fops = &ar7_wdt_fops,
  229. };
  230. static int ar7_wdt_probe(struct platform_device *pdev)
  231. {
  232. int rc;
  233. ar7_regs_wdt =
  234. platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
  235. ar7_wdt = devm_ioremap_resource(&pdev->dev, ar7_regs_wdt);
  236. if (IS_ERR(ar7_wdt))
  237. return PTR_ERR(ar7_wdt);
  238. vbus_clk = clk_get(NULL, "vbus");
  239. if (IS_ERR(vbus_clk)) {
  240. pr_err("could not get vbus clock\n");
  241. return PTR_ERR(vbus_clk);
  242. }
  243. ar7_wdt_disable_wdt();
  244. ar7_wdt_prescale(prescale_value);
  245. ar7_wdt_update_margin(margin);
  246. rc = misc_register(&ar7_wdt_miscdev);
  247. if (rc) {
  248. pr_err("unable to register misc device\n");
  249. goto out;
  250. }
  251. return 0;
  252. out:
  253. clk_put(vbus_clk);
  254. vbus_clk = NULL;
  255. return rc;
  256. }
  257. static int ar7_wdt_remove(struct platform_device *pdev)
  258. {
  259. misc_deregister(&ar7_wdt_miscdev);
  260. clk_put(vbus_clk);
  261. vbus_clk = NULL;
  262. return 0;
  263. }
  264. static void ar7_wdt_shutdown(struct platform_device *pdev)
  265. {
  266. if (!nowayout)
  267. ar7_wdt_disable_wdt();
  268. }
  269. static struct platform_driver ar7_wdt_driver = {
  270. .probe = ar7_wdt_probe,
  271. .remove = ar7_wdt_remove,
  272. .shutdown = ar7_wdt_shutdown,
  273. .driver = {
  274. .name = "ar7_wdt",
  275. },
  276. };
  277. module_platform_driver(ar7_wdt_driver);