xilinx_wwdt.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Xilinx window watchdog timer driver.
  4. *
  5. * Author(s): Michal Simek <michal.simek@xilinx.com>
  6. * Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
  7. *
  8. * Copyright (c) 2020, Xilinx Inc.
  9. */
  10. #include <clk.h>
  11. #include <common.h>
  12. #include <dm.h>
  13. #include <regmap.h>
  14. #include <wdt.h>
  15. #include <linux/compat.h>
  16. #include <dm/device_compat.h>
  17. #include <linux/io.h>
  18. /* Refresh Register Masks */
  19. #define XWT_WWREF_GWRR_MASK BIT(0) /* Refresh and start new period */
  20. /* Generic Control/Status Register Masks */
  21. #define XWT_WWCSR_GWEN_MASK BIT(0) /* Enable Bit */
  22. /* Register offsets for the WWDT device */
  23. #define XWT_WWDT_MWR_OFFSET 0x00
  24. #define XWT_WWDT_ESR_OFFSET 0x04
  25. #define XWT_WWDT_FCR_OFFSET 0x08
  26. #define XWT_WWDT_FWR_OFFSET 0x0c
  27. #define XWT_WWDT_SWR_OFFSET 0x10
  28. #define XWT_WWDT_CNT_MIN 1
  29. #define XWT_WWDT_CNT_MAX 0xffffffff
  30. /* Master Write Control Register Masks */
  31. #define XWT_WWDT_MWR_MASK BIT(0)
  32. /* Enable and Status Register Masks */
  33. #define XWT_WWDT_ESR_WINT_MASK BIT(16)
  34. #define XWT_WWDT_ESR_WSW_MASK BIT(8)
  35. #define XWT_WWDT_ESR_WEN_MASK BIT(0)
  36. struct xlnx_wwdt_priv {
  37. bool enable_once;
  38. struct regmap *regs;
  39. struct clk clk;
  40. };
  41. struct xlnx_wwdt_plat {
  42. bool enable_once;
  43. };
  44. static int xlnx_wwdt_reset(struct udevice *dev)
  45. {
  46. u32 esr;
  47. struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
  48. regmap_write(wdt->regs, XWT_WWDT_MWR_OFFSET, XWT_WWDT_MWR_MASK);
  49. regmap_read(wdt->regs, XWT_WWDT_ESR_OFFSET, &esr);
  50. esr |= XWT_WWDT_ESR_WINT_MASK;
  51. esr &= ~XWT_WWDT_ESR_WSW_MASK;
  52. regmap_write(wdt->regs, XWT_WWDT_ESR_OFFSET, esr);
  53. regmap_read(wdt->regs, XWT_WWDT_ESR_OFFSET, &esr);
  54. esr |= XWT_WWDT_ESR_WSW_MASK;
  55. regmap_write(wdt->regs, XWT_WWDT_ESR_OFFSET, esr);
  56. return 0;
  57. }
  58. static int xlnx_wwdt_stop(struct udevice *dev)
  59. {
  60. struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
  61. if (wdt->enable_once) {
  62. dev_warn(dev, "Can't stop Xilinx watchdog.\n");
  63. return -EBUSY;
  64. }
  65. /* Disable the window watchdog timer */
  66. regmap_write(wdt->regs, XWT_WWDT_MWR_OFFSET, XWT_WWDT_MWR_MASK);
  67. regmap_write(wdt->regs, XWT_WWDT_ESR_OFFSET, ~(u32)XWT_WWDT_ESR_WEN_MASK);
  68. clk_disable(&wdt->clk);
  69. dev_dbg(dev, "Watchdog disabled!\n");
  70. return 0;
  71. }
  72. static int xlnx_wwdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
  73. {
  74. int ret;
  75. u32 esr;
  76. u64 count, timeout;
  77. unsigned long clock_f;
  78. struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
  79. clock_f = clk_get_rate(&wdt->clk);
  80. if (IS_ERR_VALUE(clock_f)) {
  81. dev_err(dev, "failed to get rate\n");
  82. return clock_f;
  83. }
  84. dev_dbg(dev, "%s: CLK %ld\n", __func__, clock_f);
  85. /* Convert timeout from msec to sec */
  86. timeout = timeout_ms / 1000;
  87. /* Calculate timeout count */
  88. count = timeout * clock_f;
  89. /* Count should be at least 1 */
  90. if (count < XWT_WWDT_CNT_MIN) {
  91. debug("%s: watchdog won't fire with 0 ticks\n", __func__);
  92. count = XWT_WWDT_CNT_MIN;
  93. }
  94. /* Limit the count to maximum possible value */
  95. if (count > XWT_WWDT_CNT_MAX) {
  96. debug("%s: maximum watchdog timeout exceeded\n", __func__);
  97. count = XWT_WWDT_CNT_MAX;
  98. }
  99. ret = clk_enable(&wdt->clk);
  100. if (ret) {
  101. dev_err(dev, "failed to enable clock\n");
  102. return ret;
  103. }
  104. /* Disable the window watchdog timer */
  105. regmap_write(wdt->regs, XWT_WWDT_MWR_OFFSET, XWT_WWDT_MWR_MASK);
  106. regmap_write(wdt->regs, XWT_WWDT_ESR_OFFSET, ~(u32)XWT_WWDT_ESR_WEN_MASK);
  107. /* Set first window and second window registers with timeout */
  108. regmap_write(wdt->regs, XWT_WWDT_FWR_OFFSET, 0); /* No pre-timeout */
  109. regmap_write(wdt->regs, XWT_WWDT_SWR_OFFSET, (u32)count);
  110. regmap_write(wdt->regs, XWT_WWDT_FCR_OFFSET, 0);
  111. /* Enable the window watchdog timer */
  112. regmap_read(wdt->regs, XWT_WWDT_ESR_OFFSET, &esr);
  113. esr |= XWT_WWDT_ESR_WEN_MASK;
  114. regmap_write(wdt->regs, XWT_WWDT_ESR_OFFSET, esr);
  115. return 0;
  116. }
  117. static int xlnx_wwdt_expire_now(struct udevice *dev, ulong flags)
  118. {
  119. return xlnx_wwdt_start(dev, XWT_WWDT_CNT_MIN, flags);
  120. }
  121. static int xlnx_wwdt_probe(struct udevice *dev)
  122. {
  123. int ret;
  124. struct xlnx_wwdt_plat *plat = dev_get_plat(dev);
  125. struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
  126. dev_dbg(dev, "%s: Probing wdt%u\n", __func__, dev_seq(dev));
  127. ret = regmap_init_mem(dev_ofnode(dev), &wdt->regs);
  128. if (ret) {
  129. dev_dbg(dev, "failed to get regbase of wwdt\n");
  130. return ret;
  131. }
  132. wdt->enable_once = plat->enable_once;
  133. ret = clk_get_by_index(dev, 0, &wdt->clk);
  134. if (ret < 0)
  135. dev_err(dev, "failed to get clock\n");
  136. return ret;
  137. }
  138. static int xlnx_wwdt_of_to_plat(struct udevice *dev)
  139. {
  140. struct xlnx_wwdt_plat *plat = dev_get_plat(dev);
  141. plat->enable_once = dev_read_u32_default(dev, "xlnx,wdt-enable-once",
  142. 0);
  143. dev_dbg(dev, "wdt-enable-once %d\n", plat->enable_once);
  144. return 0;
  145. }
  146. static const struct wdt_ops xlnx_wwdt_ops = {
  147. .start = xlnx_wwdt_start,
  148. .reset = xlnx_wwdt_reset,
  149. .stop = xlnx_wwdt_stop,
  150. .expire_now = xlnx_wwdt_expire_now,
  151. };
  152. static const struct udevice_id xlnx_wwdt_ids[] = {
  153. { .compatible = "xlnx,versal-wwdt-1.0", },
  154. {},
  155. };
  156. U_BOOT_DRIVER(xlnx_wwdt) = {
  157. .name = "xlnx_wwdt",
  158. .id = UCLASS_WDT,
  159. .of_match = xlnx_wwdt_ids,
  160. .probe = xlnx_wwdt_probe,
  161. .priv_auto = sizeof(struct xlnx_wwdt_priv),
  162. .plat_auto = sizeof(struct xlnx_wwdt_plat),
  163. .of_to_plat = xlnx_wwdt_of_to_plat,
  164. .ops = &xlnx_wwdt_ops,
  165. };