visconti_wdt.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2020 TOSHIBA CORPORATION
  4. * Copyright (c) 2020 Toshiba Electronic Devices & Storage Corporation
  5. * Copyright (c) 2020 Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
  6. */
  7. #include <linux/clk.h>
  8. #include <linux/io.h>
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/watchdog.h>
  14. #define WDT_CNT 0x00
  15. #define WDT_MIN 0x04
  16. #define WDT_MAX 0x08
  17. #define WDT_CTL 0x0c
  18. #define WDT_CMD 0x10
  19. #define WDT_CMD_CLEAR 0x4352
  20. #define WDT_CMD_START_STOP 0x5354
  21. #define WDT_DIV 0x30
  22. #define VISCONTI_WDT_FREQ 2000000 /* 2MHz */
  23. #define WDT_DEFAULT_TIMEOUT 10U /* in seconds */
  24. static bool nowayout = WATCHDOG_NOWAYOUT;
  25. module_param(nowayout, bool, 0);
  26. MODULE_PARM_DESC(
  27. nowayout,
  28. "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT)")");
  29. struct visconti_wdt_priv {
  30. struct watchdog_device wdev;
  31. void __iomem *base;
  32. u32 div;
  33. };
  34. static int visconti_wdt_start(struct watchdog_device *wdev)
  35. {
  36. struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
  37. u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ;
  38. writel(priv->div, priv->base + WDT_DIV);
  39. writel(0, priv->base + WDT_MIN);
  40. writel(timeout, priv->base + WDT_MAX);
  41. writel(0, priv->base + WDT_CTL);
  42. writel(WDT_CMD_START_STOP, priv->base + WDT_CMD);
  43. return 0;
  44. }
  45. static int visconti_wdt_stop(struct watchdog_device *wdev)
  46. {
  47. struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
  48. writel(1, priv->base + WDT_CTL);
  49. writel(WDT_CMD_START_STOP, priv->base + WDT_CMD);
  50. return 0;
  51. }
  52. static int visconti_wdt_ping(struct watchdog_device *wdd)
  53. {
  54. struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdd);
  55. writel(WDT_CMD_CLEAR, priv->base + WDT_CMD);
  56. return 0;
  57. }
  58. static unsigned int visconti_wdt_get_timeleft(struct watchdog_device *wdev)
  59. {
  60. struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
  61. u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ;
  62. u32 cnt = readl(priv->base + WDT_CNT);
  63. if (timeout <= cnt)
  64. return 0;
  65. timeout -= cnt;
  66. return timeout / VISCONTI_WDT_FREQ;
  67. }
  68. static int visconti_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout)
  69. {
  70. u32 val;
  71. struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
  72. wdev->timeout = timeout;
  73. val = wdev->timeout * VISCONTI_WDT_FREQ;
  74. /* Clear counter before setting timeout because WDT expires */
  75. writel(WDT_CMD_CLEAR, priv->base + WDT_CMD);
  76. writel(val, priv->base + WDT_MAX);
  77. return 0;
  78. }
  79. static const struct watchdog_info visconti_wdt_info = {
  80. .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
  81. .identity = "Visconti Watchdog",
  82. };
  83. static const struct watchdog_ops visconti_wdt_ops = {
  84. .owner = THIS_MODULE,
  85. .start = visconti_wdt_start,
  86. .stop = visconti_wdt_stop,
  87. .ping = visconti_wdt_ping,
  88. .get_timeleft = visconti_wdt_get_timeleft,
  89. .set_timeout = visconti_wdt_set_timeout,
  90. };
  91. static void visconti_clk_disable_unprepare(void *data)
  92. {
  93. clk_disable_unprepare(data);
  94. }
  95. static int visconti_wdt_probe(struct platform_device *pdev)
  96. {
  97. struct watchdog_device *wdev;
  98. struct visconti_wdt_priv *priv;
  99. struct device *dev = &pdev->dev;
  100. struct clk *clk;
  101. int ret;
  102. unsigned long clk_freq;
  103. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  104. if (!priv)
  105. return -ENOMEM;
  106. priv->base = devm_platform_ioremap_resource(pdev, 0);
  107. if (IS_ERR(priv->base))
  108. return PTR_ERR(priv->base);
  109. clk = devm_clk_get(dev, NULL);
  110. if (IS_ERR(clk))
  111. return dev_err_probe(dev, PTR_ERR(clk), "Could not get clock\n");
  112. ret = clk_prepare_enable(clk);
  113. if (ret) {
  114. dev_err(dev, "Could not enable clock\n");
  115. return ret;
  116. }
  117. ret = devm_add_action_or_reset(dev, visconti_clk_disable_unprepare, clk);
  118. if (ret)
  119. return ret;
  120. clk_freq = clk_get_rate(clk);
  121. if (!clk_freq)
  122. return -EINVAL;
  123. priv->div = clk_freq / VISCONTI_WDT_FREQ;
  124. /* Initialize struct watchdog_device. */
  125. wdev = &priv->wdev;
  126. wdev->info = &visconti_wdt_info;
  127. wdev->ops = &visconti_wdt_ops;
  128. wdev->parent = dev;
  129. wdev->min_timeout = 1;
  130. wdev->max_timeout = 0xffffffff / VISCONTI_WDT_FREQ;
  131. wdev->timeout = min(wdev->max_timeout, WDT_DEFAULT_TIMEOUT);
  132. watchdog_set_drvdata(wdev, priv);
  133. watchdog_set_nowayout(wdev, nowayout);
  134. watchdog_stop_on_unregister(wdev);
  135. /* This overrides the default timeout only if DT configuration was found */
  136. ret = watchdog_init_timeout(wdev, 0, dev);
  137. if (ret)
  138. dev_warn(dev, "Specified timeout value invalid, using default\n");
  139. return devm_watchdog_register_device(dev, wdev);
  140. }
  141. static const struct of_device_id visconti_wdt_of_match[] = {
  142. { .compatible = "toshiba,visconti-wdt", },
  143. {}
  144. };
  145. MODULE_DEVICE_TABLE(of, visconti_wdt_of_match);
  146. static struct platform_driver visconti_wdt_driver = {
  147. .driver = {
  148. .name = "visconti_wdt",
  149. .of_match_table = visconti_wdt_of_match,
  150. },
  151. .probe = visconti_wdt_probe,
  152. };
  153. module_platform_driver(visconti_wdt_driver);
  154. MODULE_DESCRIPTION("TOSHIBA Visconti Watchdog Driver");
  155. MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp");
  156. MODULE_LICENSE("GPL v2");