meson_gxbb_wdt.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  2. /*
  3. * Copyright (c) 2016 BayLibre, SAS.
  4. * Author: Neil Armstrong <narmstrong@baylibre.com>
  5. *
  6. */
  7. #include <linux/clk.h>
  8. #include <linux/err.h>
  9. #include <linux/io.h>
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/slab.h>
  14. #include <linux/types.h>
  15. #include <linux/watchdog.h>
  16. #define DEFAULT_TIMEOUT 30 /* seconds */
  17. #define GXBB_WDT_CTRL_REG 0x0
  18. #define GXBB_WDT_TCNT_REG 0x8
  19. #define GXBB_WDT_RSET_REG 0xc
  20. #define GXBB_WDT_CTRL_CLKDIV_EN BIT(25)
  21. #define GXBB_WDT_CTRL_CLK_EN BIT(24)
  22. #define GXBB_WDT_CTRL_EN BIT(18)
  23. #define GXBB_WDT_CTRL_DIV_MASK (BIT(18) - 1)
  24. #define GXBB_WDT_TCNT_SETUP_MASK (BIT(16) - 1)
  25. #define GXBB_WDT_TCNT_CNT_SHIFT 16
  26. static bool nowayout = WATCHDOG_NOWAYOUT;
  27. module_param(nowayout, bool, 0);
  28. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started default="
  29. __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  30. static unsigned int timeout;
  31. module_param(timeout, uint, 0);
  32. MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds="
  33. __MODULE_STRING(DEFAULT_TIMEOUT) ")");
  34. struct meson_gxbb_wdt {
  35. void __iomem *reg_base;
  36. struct watchdog_device wdt_dev;
  37. struct clk *clk;
  38. };
  39. struct wdt_params {
  40. u32 rst;
  41. };
  42. static int meson_gxbb_wdt_start(struct watchdog_device *wdt_dev)
  43. {
  44. struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
  45. writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) | GXBB_WDT_CTRL_EN,
  46. data->reg_base + GXBB_WDT_CTRL_REG);
  47. return 0;
  48. }
  49. static int meson_gxbb_wdt_stop(struct watchdog_device *wdt_dev)
  50. {
  51. struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
  52. writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) & ~GXBB_WDT_CTRL_EN,
  53. data->reg_base + GXBB_WDT_CTRL_REG);
  54. return 0;
  55. }
  56. static int meson_gxbb_wdt_ping(struct watchdog_device *wdt_dev)
  57. {
  58. struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
  59. writel(0, data->reg_base + GXBB_WDT_RSET_REG);
  60. return 0;
  61. }
  62. static int meson_gxbb_wdt_set_timeout(struct watchdog_device *wdt_dev,
  63. unsigned int timeout)
  64. {
  65. struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
  66. unsigned long tcnt = timeout * 1000;
  67. if (tcnt > GXBB_WDT_TCNT_SETUP_MASK)
  68. tcnt = GXBB_WDT_TCNT_SETUP_MASK;
  69. wdt_dev->timeout = timeout;
  70. meson_gxbb_wdt_ping(wdt_dev);
  71. writel(tcnt, data->reg_base + GXBB_WDT_TCNT_REG);
  72. return 0;
  73. }
  74. static unsigned int meson_gxbb_wdt_get_timeleft(struct watchdog_device *wdt_dev)
  75. {
  76. struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
  77. unsigned long reg;
  78. reg = readl(data->reg_base + GXBB_WDT_TCNT_REG);
  79. return ((reg & GXBB_WDT_TCNT_SETUP_MASK) -
  80. (reg >> GXBB_WDT_TCNT_CNT_SHIFT)) / 1000;
  81. }
  82. static const struct watchdog_ops meson_gxbb_wdt_ops = {
  83. .start = meson_gxbb_wdt_start,
  84. .stop = meson_gxbb_wdt_stop,
  85. .ping = meson_gxbb_wdt_ping,
  86. .set_timeout = meson_gxbb_wdt_set_timeout,
  87. .get_timeleft = meson_gxbb_wdt_get_timeleft,
  88. };
  89. static const struct watchdog_info meson_gxbb_wdt_info = {
  90. .identity = "Meson GXBB Watchdog",
  91. .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  92. };
  93. static int __maybe_unused meson_gxbb_wdt_resume(struct device *dev)
  94. {
  95. struct meson_gxbb_wdt *data = dev_get_drvdata(dev);
  96. if (watchdog_active(&data->wdt_dev))
  97. meson_gxbb_wdt_start(&data->wdt_dev);
  98. return 0;
  99. }
  100. static int __maybe_unused meson_gxbb_wdt_suspend(struct device *dev)
  101. {
  102. struct meson_gxbb_wdt *data = dev_get_drvdata(dev);
  103. if (watchdog_active(&data->wdt_dev))
  104. meson_gxbb_wdt_stop(&data->wdt_dev);
  105. return 0;
  106. }
  107. static const struct dev_pm_ops meson_gxbb_wdt_pm_ops = {
  108. SET_SYSTEM_SLEEP_PM_OPS(meson_gxbb_wdt_suspend, meson_gxbb_wdt_resume)
  109. };
  110. static const struct wdt_params gxbb_params = {
  111. .rst = BIT(21),
  112. };
  113. static const struct wdt_params t7_params = {
  114. .rst = BIT(22),
  115. };
  116. static const struct of_device_id meson_gxbb_wdt_dt_ids[] = {
  117. { .compatible = "amlogic,meson-gxbb-wdt", .data = &gxbb_params, },
  118. { .compatible = "amlogic,t7-wdt", .data = &t7_params, },
  119. { /* sentinel */ },
  120. };
  121. MODULE_DEVICE_TABLE(of, meson_gxbb_wdt_dt_ids);
  122. static int meson_gxbb_wdt_probe(struct platform_device *pdev)
  123. {
  124. struct device *dev = &pdev->dev;
  125. struct meson_gxbb_wdt *data;
  126. struct wdt_params *params;
  127. u32 ctrl_reg;
  128. data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
  129. if (!data)
  130. return -ENOMEM;
  131. data->reg_base = devm_platform_ioremap_resource(pdev, 0);
  132. if (IS_ERR(data->reg_base))
  133. return PTR_ERR(data->reg_base);
  134. data->clk = devm_clk_get_enabled(dev, NULL);
  135. if (IS_ERR(data->clk))
  136. return PTR_ERR(data->clk);
  137. params = (struct wdt_params *)of_device_get_match_data(dev);
  138. platform_set_drvdata(pdev, data);
  139. data->wdt_dev.parent = dev;
  140. data->wdt_dev.info = &meson_gxbb_wdt_info;
  141. data->wdt_dev.ops = &meson_gxbb_wdt_ops;
  142. data->wdt_dev.max_hw_heartbeat_ms = GXBB_WDT_TCNT_SETUP_MASK;
  143. data->wdt_dev.min_timeout = 1;
  144. data->wdt_dev.timeout = DEFAULT_TIMEOUT;
  145. watchdog_init_timeout(&data->wdt_dev, timeout, dev);
  146. watchdog_set_nowayout(&data->wdt_dev, nowayout);
  147. watchdog_set_drvdata(&data->wdt_dev, data);
  148. ctrl_reg = readl(data->reg_base + GXBB_WDT_CTRL_REG) &
  149. GXBB_WDT_CTRL_EN;
  150. if (ctrl_reg) {
  151. /* Watchdog is running - keep it running but extend timeout
  152. * to the maximum while setting the timebase
  153. */
  154. set_bit(WDOG_HW_RUNNING, &data->wdt_dev.status);
  155. meson_gxbb_wdt_set_timeout(&data->wdt_dev,
  156. GXBB_WDT_TCNT_SETUP_MASK / 1000);
  157. }
  158. /* Setup with 1ms timebase */
  159. ctrl_reg |= ((clk_get_rate(data->clk) / 1000) &
  160. GXBB_WDT_CTRL_DIV_MASK) |
  161. params->rst |
  162. GXBB_WDT_CTRL_CLK_EN |
  163. GXBB_WDT_CTRL_CLKDIV_EN;
  164. writel(ctrl_reg, data->reg_base + GXBB_WDT_CTRL_REG);
  165. meson_gxbb_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
  166. return devm_watchdog_register_device(dev, &data->wdt_dev);
  167. }
  168. static struct platform_driver meson_gxbb_wdt_driver = {
  169. .probe = meson_gxbb_wdt_probe,
  170. .driver = {
  171. .name = "meson-gxbb-wdt",
  172. .pm = &meson_gxbb_wdt_pm_ops,
  173. .of_match_table = meson_gxbb_wdt_dt_ids,
  174. },
  175. };
  176. module_platform_driver(meson_gxbb_wdt_driver);
  177. MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
  178. MODULE_DESCRIPTION("Amlogic Meson GXBB Watchdog timer driver");
  179. MODULE_LICENSE("Dual BSD/GPL");