txx9wdt.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * txx9wdt: A Hardware Watchdog Driver for TXx9 SoCs
  3. *
  4. * Copyright (C) 2007 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
  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. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  11. #include <linux/module.h>
  12. #include <linux/moduleparam.h>
  13. #include <linux/types.h>
  14. #include <linux/miscdevice.h>
  15. #include <linux/watchdog.h>
  16. #include <linux/init.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/clk.h>
  19. #include <linux/err.h>
  20. #include <linux/io.h>
  21. #include <asm/txx9tmr.h>
  22. #define WD_TIMER_CCD 7 /* 1/256 */
  23. #define WD_TIMER_CLK (clk_get_rate(txx9_imclk) / (2 << WD_TIMER_CCD))
  24. #define WD_MAX_TIMEOUT ((0xffffffff >> (32 - TXX9_TIMER_BITS)) / WD_TIMER_CLK)
  25. #define TIMER_MARGIN 60 /* Default is 60 seconds */
  26. static unsigned int timeout = TIMER_MARGIN; /* in seconds */
  27. module_param(timeout, uint, 0);
  28. MODULE_PARM_DESC(timeout,
  29. "Watchdog timeout in seconds. "
  30. "(0<timeout<((2^" __MODULE_STRING(TXX9_TIMER_BITS) ")/(IMCLK/256)), "
  31. "default=" __MODULE_STRING(TIMER_MARGIN) ")");
  32. static bool nowayout = WATCHDOG_NOWAYOUT;
  33. module_param(nowayout, bool, 0);
  34. MODULE_PARM_DESC(nowayout,
  35. "Watchdog cannot be stopped once started "
  36. "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  37. static struct txx9_tmr_reg __iomem *txx9wdt_reg;
  38. static struct clk *txx9_imclk;
  39. static DEFINE_SPINLOCK(txx9_lock);
  40. static int txx9wdt_ping(struct watchdog_device *wdt_dev)
  41. {
  42. spin_lock(&txx9_lock);
  43. __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr);
  44. spin_unlock(&txx9_lock);
  45. return 0;
  46. }
  47. static int txx9wdt_start(struct watchdog_device *wdt_dev)
  48. {
  49. spin_lock(&txx9_lock);
  50. __raw_writel(WD_TIMER_CLK * wdt_dev->timeout, &txx9wdt_reg->cpra);
  51. __raw_writel(WD_TIMER_CCD, &txx9wdt_reg->ccdr);
  52. __raw_writel(0, &txx9wdt_reg->tisr); /* clear pending interrupt */
  53. __raw_writel(TXx9_TMTCR_TCE | TXx9_TMTCR_CCDE | TXx9_TMTCR_TMODE_WDOG,
  54. &txx9wdt_reg->tcr);
  55. __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr);
  56. spin_unlock(&txx9_lock);
  57. return 0;
  58. }
  59. static int txx9wdt_stop(struct watchdog_device *wdt_dev)
  60. {
  61. spin_lock(&txx9_lock);
  62. __raw_writel(TXx9_TMWTMR_WDIS, &txx9wdt_reg->wtmr);
  63. __raw_writel(__raw_readl(&txx9wdt_reg->tcr) & ~TXx9_TMTCR_TCE,
  64. &txx9wdt_reg->tcr);
  65. spin_unlock(&txx9_lock);
  66. return 0;
  67. }
  68. static int txx9wdt_set_timeout(struct watchdog_device *wdt_dev,
  69. unsigned int new_timeout)
  70. {
  71. wdt_dev->timeout = new_timeout;
  72. txx9wdt_stop(wdt_dev);
  73. txx9wdt_start(wdt_dev);
  74. return 0;
  75. }
  76. static const struct watchdog_info txx9wdt_info = {
  77. .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  78. .identity = "Hardware Watchdog for TXx9",
  79. };
  80. static const struct watchdog_ops txx9wdt_ops = {
  81. .owner = THIS_MODULE,
  82. .start = txx9wdt_start,
  83. .stop = txx9wdt_stop,
  84. .ping = txx9wdt_ping,
  85. .set_timeout = txx9wdt_set_timeout,
  86. };
  87. static struct watchdog_device txx9wdt = {
  88. .info = &txx9wdt_info,
  89. .ops = &txx9wdt_ops,
  90. };
  91. static int __init txx9wdt_probe(struct platform_device *dev)
  92. {
  93. struct resource *res;
  94. int ret;
  95. txx9_imclk = clk_get(NULL, "imbus_clk");
  96. if (IS_ERR(txx9_imclk)) {
  97. ret = PTR_ERR(txx9_imclk);
  98. txx9_imclk = NULL;
  99. goto exit;
  100. }
  101. ret = clk_enable(txx9_imclk);
  102. if (ret) {
  103. clk_put(txx9_imclk);
  104. txx9_imclk = NULL;
  105. goto exit;
  106. }
  107. res = platform_get_resource(dev, IORESOURCE_MEM, 0);
  108. txx9wdt_reg = devm_request_and_ioremap(&dev->dev, res);
  109. if (!txx9wdt_reg) {
  110. ret = -EBUSY;
  111. goto exit;
  112. }
  113. if (timeout < 1 || timeout > WD_MAX_TIMEOUT)
  114. timeout = TIMER_MARGIN;
  115. txx9wdt.timeout = timeout;
  116. txx9wdt.min_timeout = 1;
  117. txx9wdt.max_timeout = WD_MAX_TIMEOUT;
  118. watchdog_set_nowayout(&txx9wdt, nowayout);
  119. ret = watchdog_register_device(&txx9wdt);
  120. if (ret)
  121. goto exit;
  122. pr_info("Hardware Watchdog Timer: timeout=%d sec (max %ld) (nowayout= %d)\n",
  123. timeout, WD_MAX_TIMEOUT, nowayout);
  124. return 0;
  125. exit:
  126. if (txx9_imclk) {
  127. clk_disable(txx9_imclk);
  128. clk_put(txx9_imclk);
  129. }
  130. return ret;
  131. }
  132. static int __exit txx9wdt_remove(struct platform_device *dev)
  133. {
  134. watchdog_unregister_device(&txx9wdt);
  135. clk_disable(txx9_imclk);
  136. clk_put(txx9_imclk);
  137. return 0;
  138. }
  139. static void txx9wdt_shutdown(struct platform_device *dev)
  140. {
  141. txx9wdt_stop(&txx9wdt);
  142. }
  143. static struct platform_driver txx9wdt_driver = {
  144. .remove = __exit_p(txx9wdt_remove),
  145. .shutdown = txx9wdt_shutdown,
  146. .driver = {
  147. .name = "txx9wdt",
  148. .owner = THIS_MODULE,
  149. },
  150. };
  151. static int __init watchdog_init(void)
  152. {
  153. return platform_driver_probe(&txx9wdt_driver, txx9wdt_probe);
  154. }
  155. static void __exit watchdog_exit(void)
  156. {
  157. platform_driver_unregister(&txx9wdt_driver);
  158. }
  159. module_init(watchdog_init);
  160. module_exit(watchdog_exit);
  161. MODULE_DESCRIPTION("TXx9 Watchdog Driver");
  162. MODULE_LICENSE("GPL");
  163. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
  164. MODULE_ALIAS("platform:txx9wdt");