octeontx_wdt.c 3.0 KB

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