txx9wdt.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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/watchdog.h>
  15. #include <linux/init.h>
  16. #include <linux/platform_device.h>
  17. #include <linux/clk.h>
  18. #include <linux/err.h>
  19. #include <linux/io.h>
  20. #include <asm/txx9tmr.h>
  21. #define WD_TIMER_CCD 7 /* 1/256 */
  22. #define WD_TIMER_CLK (clk_get_rate(txx9_imclk) / (2 << WD_TIMER_CCD))
  23. #define WD_MAX_TIMEOUT ((0xffffffff >> (32 - TXX9_TIMER_BITS)) / WD_TIMER_CLK)
  24. #define TIMER_MARGIN 60 /* Default is 60 seconds */
  25. static unsigned int timeout = TIMER_MARGIN; /* in seconds */
  26. module_param(timeout, uint, 0);
  27. MODULE_PARM_DESC(timeout,
  28. "Watchdog timeout in seconds. "
  29. "(0<timeout<((2^" __MODULE_STRING(TXX9_TIMER_BITS) ")/(IMCLK/256)), "
  30. "default=" __MODULE_STRING(TIMER_MARGIN) ")");
  31. static bool nowayout = WATCHDOG_NOWAYOUT;
  32. module_param(nowayout, bool, 0);
  33. MODULE_PARM_DESC(nowayout,
  34. "Watchdog cannot be stopped once started "
  35. "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  36. static struct txx9_tmr_reg __iomem *txx9wdt_reg;
  37. static struct clk *txx9_imclk;
  38. static DEFINE_SPINLOCK(txx9_lock);
  39. static int txx9wdt_ping(struct watchdog_device *wdt_dev)
  40. {
  41. spin_lock(&txx9_lock);
  42. __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr);
  43. spin_unlock(&txx9_lock);
  44. return 0;
  45. }
  46. static int txx9wdt_start(struct watchdog_device *wdt_dev)
  47. {
  48. spin_lock(&txx9_lock);
  49. __raw_writel(WD_TIMER_CLK * wdt_dev->timeout, &txx9wdt_reg->cpra);
  50. __raw_writel(WD_TIMER_CCD, &txx9wdt_reg->ccdr);
  51. __raw_writel(0, &txx9wdt_reg->tisr); /* clear pending interrupt */
  52. __raw_writel(TXx9_TMTCR_TCE | TXx9_TMTCR_CCDE | TXx9_TMTCR_TMODE_WDOG,
  53. &txx9wdt_reg->tcr);
  54. __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr);
  55. spin_unlock(&txx9_lock);
  56. return 0;
  57. }
  58. static int txx9wdt_stop(struct watchdog_device *wdt_dev)
  59. {
  60. spin_lock(&txx9_lock);
  61. __raw_writel(TXx9_TMWTMR_WDIS, &txx9wdt_reg->wtmr);
  62. __raw_writel(__raw_readl(&txx9wdt_reg->tcr) & ~TXx9_TMTCR_TCE,
  63. &txx9wdt_reg->tcr);
  64. spin_unlock(&txx9_lock);
  65. return 0;
  66. }
  67. static int txx9wdt_set_timeout(struct watchdog_device *wdt_dev,
  68. unsigned int new_timeout)
  69. {
  70. wdt_dev->timeout = new_timeout;
  71. txx9wdt_stop(wdt_dev);
  72. txx9wdt_start(wdt_dev);
  73. return 0;
  74. }
  75. static const struct watchdog_info txx9wdt_info = {
  76. .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  77. .identity = "Hardware Watchdog for TXx9",
  78. };
  79. static const struct watchdog_ops txx9wdt_ops = {
  80. .owner = THIS_MODULE,
  81. .start = txx9wdt_start,
  82. .stop = txx9wdt_stop,
  83. .ping = txx9wdt_ping,
  84. .set_timeout = txx9wdt_set_timeout,
  85. };
  86. static struct watchdog_device txx9wdt = {
  87. .info = &txx9wdt_info,
  88. .ops = &txx9wdt_ops,
  89. };
  90. static int __init txx9wdt_probe(struct platform_device *dev)
  91. {
  92. struct resource *res;
  93. int ret;
  94. txx9_imclk = clk_get(NULL, "imbus_clk");
  95. if (IS_ERR(txx9_imclk)) {
  96. ret = PTR_ERR(txx9_imclk);
  97. txx9_imclk = NULL;
  98. goto exit;
  99. }
  100. ret = clk_enable(txx9_imclk);
  101. if (ret) {
  102. clk_put(txx9_imclk);
  103. txx9_imclk = NULL;
  104. goto exit;
  105. }
  106. res = platform_get_resource(dev, IORESOURCE_MEM, 0);
  107. txx9wdt_reg = devm_ioremap_resource(&dev->dev, res);
  108. if (IS_ERR(txx9wdt_reg)) {
  109. ret = PTR_ERR(txx9wdt_reg);
  110. goto exit;
  111. }
  112. if (timeout < 1 || timeout > WD_MAX_TIMEOUT)
  113. timeout = TIMER_MARGIN;
  114. txx9wdt.timeout = timeout;
  115. txx9wdt.min_timeout = 1;
  116. txx9wdt.max_timeout = WD_MAX_TIMEOUT;
  117. txx9wdt.parent = &dev->dev;
  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. },
  149. };
  150. module_platform_driver_probe(txx9wdt_driver, txx9wdt_probe);
  151. MODULE_DESCRIPTION("TXx9 Watchdog Driver");
  152. MODULE_LICENSE("GPL");
  153. MODULE_ALIAS("platform:txx9wdt");