tangox_wdt.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * Copyright (C) 2015 Mans Rullgard <[email protected]>
  3. * SMP86xx/SMP87xx Watchdog driver
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the
  7. * Free Software Foundation; either version 2 of the License, or (at your
  8. * option) any later version.
  9. */
  10. #include <linux/bitops.h>
  11. #include <linux/clk.h>
  12. #include <linux/delay.h>
  13. #include <linux/io.h>
  14. #include <linux/kernel.h>
  15. #include <linux/module.h>
  16. #include <linux/moduleparam.h>
  17. #include <linux/notifier.h>
  18. #include <linux/platform_device.h>
  19. #include <linux/reboot.h>
  20. #include <linux/watchdog.h>
  21. #define DEFAULT_TIMEOUT 30
  22. static bool nowayout = WATCHDOG_NOWAYOUT;
  23. module_param(nowayout, bool, 0);
  24. MODULE_PARM_DESC(nowayout,
  25. "Watchdog cannot be stopped once started (default="
  26. __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  27. static unsigned int timeout;
  28. module_param(timeout, int, 0);
  29. MODULE_PARM_DESC(timeout, "Watchdog timeout");
  30. /*
  31. * Counter counts down from programmed value. Reset asserts when
  32. * the counter reaches 1.
  33. */
  34. #define WD_COUNTER 0
  35. #define WD_CONFIG 4
  36. #define WD_CONFIG_XTAL_IN BIT(0)
  37. #define WD_CONFIG_DISABLE BIT(31)
  38. struct tangox_wdt_device {
  39. struct watchdog_device wdt;
  40. void __iomem *base;
  41. unsigned long clk_rate;
  42. struct clk *clk;
  43. struct notifier_block restart;
  44. };
  45. static int tangox_wdt_set_timeout(struct watchdog_device *wdt,
  46. unsigned int new_timeout)
  47. {
  48. wdt->timeout = new_timeout;
  49. return 0;
  50. }
  51. static int tangox_wdt_start(struct watchdog_device *wdt)
  52. {
  53. struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt);
  54. u32 ticks;
  55. ticks = 1 + wdt->timeout * dev->clk_rate;
  56. writel(ticks, dev->base + WD_COUNTER);
  57. return 0;
  58. }
  59. static int tangox_wdt_stop(struct watchdog_device *wdt)
  60. {
  61. struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt);
  62. writel(0, dev->base + WD_COUNTER);
  63. return 0;
  64. }
  65. static unsigned int tangox_wdt_get_timeleft(struct watchdog_device *wdt)
  66. {
  67. struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt);
  68. u32 count;
  69. count = readl(dev->base + WD_COUNTER);
  70. if (!count)
  71. return 0;
  72. return (count - 1) / dev->clk_rate;
  73. }
  74. static const struct watchdog_info tangox_wdt_info = {
  75. .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  76. .identity = "tangox watchdog",
  77. };
  78. static const struct watchdog_ops tangox_wdt_ops = {
  79. .start = tangox_wdt_start,
  80. .stop = tangox_wdt_stop,
  81. .set_timeout = tangox_wdt_set_timeout,
  82. .get_timeleft = tangox_wdt_get_timeleft,
  83. };
  84. static int tangox_wdt_restart(struct notifier_block *nb, unsigned long action,
  85. void *data)
  86. {
  87. struct tangox_wdt_device *dev =
  88. container_of(nb, struct tangox_wdt_device, restart);
  89. writel(1, dev->base + WD_COUNTER);
  90. return NOTIFY_DONE;
  91. }
  92. static int tangox_wdt_probe(struct platform_device *pdev)
  93. {
  94. struct tangox_wdt_device *dev;
  95. struct resource *res;
  96. u32 config;
  97. int err;
  98. dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
  99. if (!dev)
  100. return -ENOMEM;
  101. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  102. dev->base = devm_ioremap_resource(&pdev->dev, res);
  103. if (IS_ERR(dev->base))
  104. return PTR_ERR(dev->base);
  105. dev->clk = devm_clk_get(&pdev->dev, NULL);
  106. if (IS_ERR(dev->clk))
  107. return PTR_ERR(dev->clk);
  108. err = clk_prepare_enable(dev->clk);
  109. if (err)
  110. return err;
  111. dev->clk_rate = clk_get_rate(dev->clk);
  112. if (!dev->clk_rate) {
  113. err = -EINVAL;
  114. goto err;
  115. }
  116. dev->wdt.parent = &pdev->dev;
  117. dev->wdt.info = &tangox_wdt_info;
  118. dev->wdt.ops = &tangox_wdt_ops;
  119. dev->wdt.timeout = DEFAULT_TIMEOUT;
  120. dev->wdt.min_timeout = 1;
  121. dev->wdt.max_hw_heartbeat_ms = (U32_MAX - 1) / dev->clk_rate;
  122. watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev);
  123. watchdog_set_nowayout(&dev->wdt, nowayout);
  124. watchdog_set_drvdata(&dev->wdt, dev);
  125. /*
  126. * Deactivate counter if disable bit is set to avoid
  127. * accidental reset.
  128. */
  129. config = readl(dev->base + WD_CONFIG);
  130. if (config & WD_CONFIG_DISABLE)
  131. writel(0, dev->base + WD_COUNTER);
  132. writel(WD_CONFIG_XTAL_IN, dev->base + WD_CONFIG);
  133. /*
  134. * Mark as active and restart with configured timeout if
  135. * already running.
  136. */
  137. if (readl(dev->base + WD_COUNTER)) {
  138. set_bit(WDOG_HW_RUNNING, &dev->wdt.status);
  139. tangox_wdt_start(&dev->wdt);
  140. }
  141. err = watchdog_register_device(&dev->wdt);
  142. if (err)
  143. goto err;
  144. platform_set_drvdata(pdev, dev);
  145. dev->restart.notifier_call = tangox_wdt_restart;
  146. dev->restart.priority = 128;
  147. err = register_restart_handler(&dev->restart);
  148. if (err)
  149. dev_warn(&pdev->dev, "failed to register restart handler\n");
  150. dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n");
  151. return 0;
  152. err:
  153. clk_disable_unprepare(dev->clk);
  154. return err;
  155. }
  156. static int tangox_wdt_remove(struct platform_device *pdev)
  157. {
  158. struct tangox_wdt_device *dev = platform_get_drvdata(pdev);
  159. tangox_wdt_stop(&dev->wdt);
  160. clk_disable_unprepare(dev->clk);
  161. unregister_restart_handler(&dev->restart);
  162. watchdog_unregister_device(&dev->wdt);
  163. return 0;
  164. }
  165. static const struct of_device_id tangox_wdt_dt_ids[] = {
  166. { .compatible = "sigma,smp8642-wdt" },
  167. { .compatible = "sigma,smp8759-wdt" },
  168. { }
  169. };
  170. MODULE_DEVICE_TABLE(of, tangox_wdt_dt_ids);
  171. static struct platform_driver tangox_wdt_driver = {
  172. .probe = tangox_wdt_probe,
  173. .remove = tangox_wdt_remove,
  174. .driver = {
  175. .name = "tangox-wdt",
  176. .of_match_table = tangox_wdt_dt_ids,
  177. },
  178. };
  179. module_platform_driver(tangox_wdt_driver);
  180. MODULE_AUTHOR("Mans Rullgard <[email protected]>");
  181. MODULE_DESCRIPTION("SMP86xx/SMP87xx Watchdog driver");
  182. MODULE_LICENSE("GPL");