rti_wdt.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (c) Siemens AG, 2020
  4. *
  5. * Authors:
  6. * Jan Kiszka <jan.kiszka@siemens.com>
  7. *
  8. * Derived from linux/drivers/watchdog/rti_wdt.c
  9. */
  10. #include <common.h>
  11. #include <clk.h>
  12. #include <dm.h>
  13. #include <power-domain.h>
  14. #include <wdt.h>
  15. #include <asm/io.h>
  16. /* Timer register set definition */
  17. #define RTIDWDCTRL 0x90
  18. #define RTIDWDPRLD 0x94
  19. #define RTIWDSTATUS 0x98
  20. #define RTIWDKEY 0x9c
  21. #define RTIDWDCNTR 0xa0
  22. #define RTIWWDRXCTRL 0xa4
  23. #define RTIWWDSIZECTRL 0xa8
  24. #define RTIWWDRX_NMI 0xa
  25. #define RTIWWDSIZE_50P 0x50
  26. #define WDENABLE_KEY 0xa98559da
  27. #define WDKEY_SEQ0 0xe51a
  28. #define WDKEY_SEQ1 0xa35c
  29. #define WDT_PRELOAD_SHIFT 13
  30. #define WDT_PRELOAD_MAX 0xfff
  31. struct rti_wdt_priv {
  32. phys_addr_t regs;
  33. unsigned int clk_khz;
  34. };
  35. static int rti_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
  36. {
  37. struct rti_wdt_priv *priv = dev_get_priv(dev);
  38. u32 timer_margin;
  39. int ret;
  40. if (readl(priv->regs + RTIDWDCTRL) == WDENABLE_KEY)
  41. return -EBUSY;
  42. timer_margin = timeout_ms * priv->clk_khz / 1000;
  43. timer_margin >>= WDT_PRELOAD_SHIFT;
  44. if (timer_margin > WDT_PRELOAD_MAX)
  45. timer_margin = WDT_PRELOAD_MAX;
  46. writel(timer_margin, priv->regs + RTIDWDPRLD);
  47. writel(RTIWWDRX_NMI, priv->regs + RTIWWDRXCTRL);
  48. writel(RTIWWDSIZE_50P, priv->regs + RTIWWDSIZECTRL);
  49. readl(priv->regs + RTIWWDSIZECTRL);
  50. writel(WDENABLE_KEY, priv->regs + RTIDWDCTRL);
  51. return 0;
  52. }
  53. static int rti_wdt_reset(struct udevice *dev)
  54. {
  55. struct rti_wdt_priv *priv = dev_get_priv(dev);
  56. u32 prld;
  57. /* Make sure we do not reset too early */
  58. prld = readl(priv->regs + RTIDWDPRLD) << WDT_PRELOAD_SHIFT;
  59. if (readl(priv->regs + RTIDWDCNTR) >= prld / 2)
  60. return -EPERM;
  61. writel(WDKEY_SEQ0, priv->regs + RTIWDKEY);
  62. writel(WDKEY_SEQ1, priv->regs + RTIWDKEY);
  63. return 0;
  64. }
  65. static int rti_wdt_probe(struct udevice *dev)
  66. {
  67. struct rti_wdt_priv *priv = dev_get_priv(dev);
  68. struct clk clk;
  69. int ret;
  70. priv->regs = devfdt_get_addr(dev);
  71. if (!priv->regs)
  72. return -EINVAL;
  73. ret = clk_get_by_index(dev, 0, &clk);
  74. if (ret)
  75. return ret;
  76. priv->clk_khz = clk_get_rate(&clk);
  77. return 0;
  78. }
  79. static const struct wdt_ops rti_wdt_ops = {
  80. .start = rti_wdt_start,
  81. .reset = rti_wdt_reset,
  82. };
  83. static const struct udevice_id rti_wdt_ids[] = {
  84. { .compatible = "ti,j7-rti-wdt" },
  85. { }
  86. };
  87. U_BOOT_DRIVER(rti_wdt) = {
  88. .name = "rti_wdt",
  89. .id = UCLASS_WDT,
  90. .of_match = rti_wdt_ids,
  91. .ops = &rti_wdt_ops,
  92. .probe = rti_wdt_probe,
  93. .priv_auto_alloc_size = sizeof(struct rti_wdt_priv),
  94. .flags = DM_FLAG_REMOVE_WITH_PD_ON,
  95. };