octeontx_wdt.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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_WDOG_OFFSET 0x40000
  16. #define CORE0_POKE_OFFSET 0x50000
  17. #define CORE0_POKE_OFFSET_MASK 0xfffffULL
  18. #define WDOG_MODE GENMASK_ULL(1, 0)
  19. #define WDOG_LEN GENMASK_ULL(19, 4)
  20. #define WDOG_CNT GENMASK_ULL(43, 20)
  21. struct octeontx_wdt {
  22. void __iomem *reg;
  23. struct clk clk;
  24. };
  25. static int octeontx_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
  26. {
  27. struct octeontx_wdt *priv = dev_get_priv(dev);
  28. u64 clk_rate, val;
  29. u64 tout_wdog;
  30. clk_rate = clk_get_rate(&priv->clk);
  31. if (IS_ERR_VALUE(clk_rate))
  32. return -EINVAL;
  33. /* Watchdog counts in 1024 cycle steps */
  34. tout_wdog = (clk_rate * timeout_ms / 1000) >> 10;
  35. /*
  36. * We can only specify the upper 16 bits of a 24 bit value.
  37. * Round up
  38. */
  39. tout_wdog = (tout_wdog + 0xff) >> 8;
  40. /* If the timeout overflows the hardware limit, set max */
  41. if (tout_wdog >= 0x10000)
  42. tout_wdog = 0xffff;
  43. val = FIELD_PREP(WDOG_MODE, 0x3) |
  44. FIELD_PREP(WDOG_LEN, tout_wdog) |
  45. FIELD_PREP(WDOG_CNT, tout_wdog << 8);
  46. writeq(val, priv->reg + CORE0_WDOG_OFFSET);
  47. return 0;
  48. }
  49. static int octeontx_wdt_stop(struct udevice *dev)
  50. {
  51. struct octeontx_wdt *priv = dev_get_priv(dev);
  52. writeq(0, priv->reg + CORE0_WDOG_OFFSET);
  53. return 0;
  54. }
  55. static int octeontx_wdt_expire_now(struct udevice *dev, ulong flags)
  56. {
  57. octeontx_wdt_stop(dev);
  58. /* Start with 100ms timeout to expire immediately */
  59. octeontx_wdt_start(dev, 100, flags);
  60. return 0;
  61. }
  62. static int octeontx_wdt_reset(struct udevice *dev)
  63. {
  64. struct octeontx_wdt *priv = dev_get_priv(dev);
  65. writeq(~0ULL, priv->reg + CORE0_POKE_OFFSET);
  66. return 0;
  67. }
  68. static int octeontx_wdt_remove(struct udevice *dev)
  69. {
  70. octeontx_wdt_stop(dev);
  71. return 0;
  72. }
  73. static int octeontx_wdt_probe(struct udevice *dev)
  74. {
  75. struct octeontx_wdt *priv = dev_get_priv(dev);
  76. int ret;
  77. priv->reg = dev_remap_addr(dev);
  78. if (!priv->reg)
  79. return -EINVAL;
  80. /*
  81. * Save base register address in reg masking lower 20 bits
  82. * as 0xa0000 appears when extracted from the DT
  83. */
  84. priv->reg = (void __iomem *)(((u64)priv->reg &
  85. ~CORE0_POKE_OFFSET_MASK));
  86. ret = clk_get_by_index(dev, 0, &priv->clk);
  87. if (ret < 0)
  88. return ret;
  89. ret = clk_enable(&priv->clk);
  90. if (ret)
  91. return ret;
  92. return 0;
  93. }
  94. static const struct wdt_ops octeontx_wdt_ops = {
  95. .reset = octeontx_wdt_reset,
  96. .start = octeontx_wdt_start,
  97. .stop = octeontx_wdt_stop,
  98. .expire_now = octeontx_wdt_expire_now,
  99. };
  100. static const struct udevice_id octeontx_wdt_ids[] = {
  101. { .compatible = "arm,sbsa-gwdt" },
  102. {}
  103. };
  104. U_BOOT_DRIVER(wdt_octeontx) = {
  105. .name = "wdt_octeontx",
  106. .id = UCLASS_WDT,
  107. .of_match = octeontx_wdt_ids,
  108. .ops = &octeontx_wdt_ops,
  109. .priv_auto = sizeof(struct octeontx_wdt),
  110. .probe = octeontx_wdt_probe,
  111. .remove = octeontx_wdt_remove,
  112. .flags = DM_FLAG_OS_PREPARE,
  113. };