octeontx_wdt.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2019 Marvell International Ltd.
  4. *
  5. * https://spdx.org/licenses
  6. */
  7. #include <clk.h>
  8. #include <dm.h>
  9. #include <errno.h>
  10. #include <wdt.h>
  11. #include <asm/global_data.h>
  12. #include <asm/io.h>
  13. #include <linux/bitfield.h>
  14. DECLARE_GLOBAL_DATA_PTR;
  15. #define CORE0_POKE_OFFSET_MASK 0xfffffULL
  16. #define WDOG_MODE GENMASK_ULL(1, 0)
  17. #define WDOG_LEN GENMASK_ULL(19, 4)
  18. #define WDOG_CNT GENMASK_ULL(43, 20)
  19. struct octeontx_wdt_data {
  20. u32 wdog_offset;
  21. u32 poke_offset;
  22. int timer_shift;
  23. bool has_clk;
  24. };
  25. struct octeontx_wdt {
  26. void __iomem *reg;
  27. const struct octeontx_wdt_data *data;
  28. struct clk clk;
  29. };
  30. static int octeontx_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
  31. {
  32. struct octeontx_wdt *priv = dev_get_priv(dev);
  33. u64 clk_rate, val;
  34. u64 tout_wdog;
  35. if (priv->data->has_clk) {
  36. clk_rate = clk_get_rate(&priv->clk);
  37. if (IS_ERR_VALUE(clk_rate))
  38. return -EINVAL;
  39. } else {
  40. clk_rate = gd->bus_clk;
  41. }
  42. /* Watchdog counts in configured cycle steps */
  43. tout_wdog = (clk_rate * timeout_ms / 1000) >> priv->data->timer_shift;
  44. /*
  45. * We can only specify the upper 16 bits of a 24 bit value.
  46. * Round up
  47. */
  48. tout_wdog = (tout_wdog + 0xff) >> 8;
  49. /* If the timeout overflows the hardware limit, set max */
  50. if (tout_wdog >= 0x10000)
  51. tout_wdog = 0xffff;
  52. val = FIELD_PREP(WDOG_MODE, 0x3) |
  53. FIELD_PREP(WDOG_LEN, tout_wdog) |
  54. FIELD_PREP(WDOG_CNT, tout_wdog << 8);
  55. writeq(val, priv->reg + priv->data->wdog_offset);
  56. return 0;
  57. }
  58. static int octeontx_wdt_stop(struct udevice *dev)
  59. {
  60. struct octeontx_wdt *priv = dev_get_priv(dev);
  61. writeq(0, priv->reg + priv->data->wdog_offset);
  62. return 0;
  63. }
  64. static int octeontx_wdt_expire_now(struct udevice *dev, ulong flags)
  65. {
  66. octeontx_wdt_stop(dev);
  67. /* Start with 100ms timeout to expire immediately */
  68. octeontx_wdt_start(dev, 100, flags);
  69. return 0;
  70. }
  71. static int octeontx_wdt_reset(struct udevice *dev)
  72. {
  73. struct octeontx_wdt *priv = dev_get_priv(dev);
  74. writeq(~0ULL, priv->reg + priv->data->poke_offset);
  75. return 0;
  76. }
  77. static int octeontx_wdt_remove(struct udevice *dev)
  78. {
  79. octeontx_wdt_stop(dev);
  80. return 0;
  81. }
  82. static int octeontx_wdt_probe(struct udevice *dev)
  83. {
  84. struct octeontx_wdt *priv = dev_get_priv(dev);
  85. int ret;
  86. priv->reg = dev_remap_addr(dev);
  87. if (!priv->reg)
  88. return -EINVAL;
  89. priv->data = (void *)dev_get_driver_data(dev);
  90. if (!priv->data)
  91. return -EINVAL;
  92. /*
  93. * Save base register address in reg masking lower 20 bits
  94. * as 0xa0000 appears when extracted from the DT
  95. */
  96. priv->reg = (void __iomem *)(((u64)priv->reg &
  97. ~CORE0_POKE_OFFSET_MASK));
  98. if (priv->data->has_clk) {
  99. ret = clk_get_by_index(dev, 0, &priv->clk);
  100. if (ret < 0)
  101. return ret;
  102. ret = clk_enable(&priv->clk);
  103. if (ret)
  104. return ret;
  105. }
  106. return 0;
  107. }
  108. static const struct wdt_ops octeontx_wdt_ops = {
  109. .reset = octeontx_wdt_reset,
  110. .start = octeontx_wdt_start,
  111. .stop = octeontx_wdt_stop,
  112. .expire_now = octeontx_wdt_expire_now,
  113. };
  114. static const struct octeontx_wdt_data octeontx_data = {
  115. .wdog_offset = 0x40000,
  116. .poke_offset = 0x50000,
  117. .timer_shift = 10,
  118. .has_clk = true,
  119. };
  120. static const struct octeontx_wdt_data octeon_data = {
  121. .wdog_offset = 0x20000,
  122. .poke_offset = 0x30000,
  123. .timer_shift = 10,
  124. .has_clk = false,
  125. };
  126. static const struct udevice_id octeontx_wdt_ids[] = {
  127. { .compatible = "arm,sbsa-gwdt", .data = (ulong)&octeontx_data },
  128. { .compatible = "cavium,octeon-7890-ciu3", .data = (ulong)&octeon_data },
  129. {}
  130. };
  131. U_BOOT_DRIVER(wdt_octeontx) = {
  132. .name = "wdt_octeontx",
  133. .id = UCLASS_WDT,
  134. .of_match = octeontx_wdt_ids,
  135. .ops = &octeontx_wdt_ops,
  136. .priv_auto = sizeof(struct octeontx_wdt),
  137. .probe = octeontx_wdt_probe,
  138. .remove = octeontx_wdt_remove,
  139. .flags = DM_FLAG_OS_PREPARE,
  140. };