meson_gxbb_wdt.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (c) 2022 BayLibre, SAS.
  4. */
  5. #include <clk.h>
  6. #include <dm.h>
  7. #include <dm/device_compat.h>
  8. #include <reset.h>
  9. #include <wdt.h>
  10. #include <asm/io.h>
  11. #include <linux/bitops.h>
  12. #define GXBB_WDT_CTRL_REG 0x0
  13. #define GXBB_WDT_TCNT_REG 0x8
  14. #define GXBB_WDT_RSET_REG 0xc
  15. #define GXBB_WDT_CTRL_SYS_RESET_NOW BIT(26)
  16. #define GXBB_WDT_CTRL_CLKDIV_EN BIT(25)
  17. #define GXBB_WDT_CTRL_CLK_EN BIT(24)
  18. #define GXBB_WDT_CTRL_EE_RESET BIT(21)
  19. #define GXBB_WDT_CTRL_EN BIT(18)
  20. #define GXBB_WDT_CTRL_DIV_MASK GENMASK(17, 0)
  21. #define GXBB_WDT_TCNT_SETUP_MASK GENMASK(15, 0)
  22. struct amlogic_wdt_priv {
  23. void __iomem *reg_base;
  24. };
  25. static int amlogic_wdt_set_timeout(struct udevice *dev, u64 timeout_ms)
  26. {
  27. struct amlogic_wdt_priv *data = dev_get_priv(dev);
  28. if (timeout_ms > GXBB_WDT_TCNT_SETUP_MASK) {
  29. dev_warn(dev, "%s: timeout_ms=%llu: maximum watchdog timeout exceeded\n",
  30. __func__, timeout_ms);
  31. timeout_ms = GXBB_WDT_TCNT_SETUP_MASK;
  32. }
  33. writel(timeout_ms, data->reg_base + GXBB_WDT_TCNT_REG);
  34. return 0;
  35. }
  36. static int amlogic_wdt_stop(struct udevice *dev)
  37. {
  38. struct amlogic_wdt_priv *data = dev_get_priv(dev);
  39. writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) & ~GXBB_WDT_CTRL_EN,
  40. data->reg_base + GXBB_WDT_CTRL_REG);
  41. return 0;
  42. }
  43. static int amlogic_wdt_start(struct udevice *dev, u64 time_ms, ulong flags)
  44. {
  45. struct amlogic_wdt_priv *data = dev_get_priv(dev);
  46. writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) | GXBB_WDT_CTRL_EN,
  47. data->reg_base + GXBB_WDT_CTRL_REG);
  48. return amlogic_wdt_set_timeout(dev, time_ms);
  49. }
  50. static int amlogic_wdt_reset(struct udevice *dev)
  51. {
  52. struct amlogic_wdt_priv *data = dev_get_priv(dev);
  53. writel(0, data->reg_base + GXBB_WDT_RSET_REG);
  54. return 0;
  55. }
  56. static int amlogic_wdt_expire_now(struct udevice *dev, ulong flags)
  57. {
  58. struct amlogic_wdt_priv *data = dev_get_priv(dev);
  59. writel(0, data->reg_base + GXBB_WDT_CTRL_SYS_RESET_NOW);
  60. return 0;
  61. }
  62. static int amlogic_wdt_probe(struct udevice *dev)
  63. {
  64. struct amlogic_wdt_priv *data = dev_get_priv(dev);
  65. int ret;
  66. data->reg_base = dev_remap_addr(dev);
  67. if (!data->reg_base)
  68. return -EINVAL;
  69. struct clk clk;
  70. ret = clk_get_by_index(dev, 0, &clk);
  71. if (ret)
  72. return ret;
  73. ret = clk_enable(&clk);
  74. if (ret) {
  75. clk_free(&clk);
  76. return ret;
  77. }
  78. /* Setup with 1ms timebase */
  79. writel(((clk_get_rate(&clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) |
  80. GXBB_WDT_CTRL_EE_RESET |
  81. GXBB_WDT_CTRL_CLK_EN |
  82. GXBB_WDT_CTRL_CLKDIV_EN,
  83. data->reg_base + GXBB_WDT_CTRL_REG);
  84. return 0;
  85. }
  86. static const struct wdt_ops amlogic_wdt_ops = {
  87. .start = amlogic_wdt_start,
  88. .reset = amlogic_wdt_reset,
  89. .stop = amlogic_wdt_stop,
  90. .expire_now = amlogic_wdt_expire_now,
  91. };
  92. static const struct udevice_id amlogic_wdt_ids[] = {
  93. { .compatible = "amlogic,meson-gxbb-wdt" },
  94. {}
  95. };
  96. U_BOOT_DRIVER(amlogic_wdt) = {
  97. .name = "amlogic_wdt",
  98. .id = UCLASS_WDT,
  99. .of_match = amlogic_wdt_ids,
  100. .priv_auto = sizeof(struct amlogic_wdt_priv),
  101. .probe = amlogic_wdt_probe,
  102. .ops = &amlogic_wdt_ops,
  103. .flags = DM_FLAG_PRE_RELOC,
  104. };