drv_wdt.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. * Copyright (c) 2006-2025, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2022-01-25 iysheng first version
  9. * 2025-10-09 kurisaw fix inaccurate WDT clock timeout feeding issue
  10. */
  11. #include <rtthread.h>
  12. #include <rtdevice.h>
  13. #include <rthw.h>
  14. #include <board.h>
  15. #define DBG_TAG "drv.wdt"
  16. #define DBG_LVL DBG_LOG
  17. #include <rtdbg.h>
  18. #ifdef RT_USING_WDT
  19. /* User-configurable macros for WDT clock source and prescaler */
  20. /* Default select oscillator type: RCU_LXTAL */
  21. #define WDT_OSCI_TYPE RCU_LXTAL
  22. /* Prescaler divider value (must match WDT_PSC_VALUE) */
  23. #define WDT_PSC_DIVIDER 256U
  24. /* Prescaler register value */
  25. #define WDT_PSC_VALUE FWDGT_PSC_DIV256
  26. /* Derived values */
  27. #define WDT_CLOCK_FREQ LXTAL_VALUE
  28. #define WDT_TICK_HZ (WDT_CLOCK_FREQ / WDT_PSC_DIVIDER)
  29. #define WDT_MAX_RELOAD_VALUE 0xfffU
  30. typedef struct
  31. {
  32. struct rt_watchdog_device wdt;
  33. rt_uint32_t min_threshold_s;
  34. rt_uint32_t max_threshold_s;
  35. rt_uint32_t current_threshold_s;
  36. } gd32_wdt_device_t;
  37. static gd32_wdt_device_t g_wdt_dev;
  38. static rt_err_t gd32_wdt_init(rt_watchdog_t *wdt)
  39. {
  40. #if defined(SOC_SERIES_GD32H7xx)
  41. /* Enable and wait for oscillator stabilization */
  42. rcu_osci_on(WDT_OSCI_TYPE);
  43. if (ERROR == rcu_osci_stab_wait(WDT_OSCI_TYPE))
  44. {
  45. LOG_E("failed init %u clock for free watchdog.", WDT_OSCI_TYPE);
  46. return -RT_EINVAL;
  47. }
  48. #endif
  49. /* Calculate thresholds */
  50. g_wdt_dev.min_threshold_s = 1U;
  51. g_wdt_dev.max_threshold_s = ((WDT_MAX_RELOAD_VALUE + 1U) * WDT_PSC_DIVIDER) / WDT_CLOCK_FREQ;
  52. LOG_I("WDT clock: %u Hz (tick: %u Hz), threshold section [%u, %u]",
  53. WDT_CLOCK_FREQ, WDT_TICK_HZ,
  54. g_wdt_dev.min_threshold_s, g_wdt_dev.max_threshold_s);
  55. /* Configure FWDGT with max timeout */
  56. fwdgt_write_enable();
  57. if (fwdgt_config(WDT_MAX_RELOAD_VALUE, WDT_PSC_VALUE) != SUCCESS)
  58. {
  59. LOG_E("failed to configure FWDGT");
  60. return -RT_ERROR;
  61. }
  62. fwdgt_enable();
  63. return RT_EOK;
  64. }
  65. static rt_err_t gd32_wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
  66. {
  67. rt_uint32_t param;
  68. switch (cmd)
  69. {
  70. case RT_DEVICE_CTRL_WDT_KEEPALIVE:
  71. fwdgt_counter_reload();
  72. break;
  73. case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
  74. param = *(rt_uint32_t *)arg;
  75. if ((param > g_wdt_dev.max_threshold_s) ||
  76. (param < g_wdt_dev.min_threshold_s))
  77. {
  78. LOG_E("invalid param@%u (out of [%u, %u])", param,
  79. g_wdt_dev.min_threshold_s, g_wdt_dev.max_threshold_s);
  80. return -RT_EINVAL;
  81. }
  82. else
  83. {
  84. g_wdt_dev.current_threshold_s = param;
  85. rt_uint32_t reload_value = (param * WDT_TICK_HZ) - 1U;
  86. fwdgt_write_enable();
  87. if (fwdgt_config(reload_value, WDT_PSC_VALUE) != SUCCESS)
  88. {
  89. LOG_E("failed to set timeout %u s", param);
  90. return -RT_ERROR;
  91. }
  92. fwdgt_write_disable();
  93. }
  94. break;
  95. case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
  96. *(rt_uint32_t *)arg = g_wdt_dev.current_threshold_s;
  97. break;
  98. case RT_DEVICE_CTRL_WDT_START:
  99. fwdgt_enable();
  100. break;
  101. default:
  102. LOG_W("This command is not supported.");
  103. return -RT_ERROR;
  104. }
  105. return RT_EOK;
  106. }
  107. static struct rt_watchdog_ops g_wdt_ops = {
  108. gd32_wdt_init,
  109. gd32_wdt_control,
  110. };
  111. static int rt_hw_wdt_init(void)
  112. {
  113. rt_err_t ret = RT_EOK;
  114. g_wdt_dev.wdt.ops = &g_wdt_ops;
  115. /* register watchdog device */
  116. if (rt_hw_watchdog_register(&g_wdt_dev.wdt, "wdt",
  117. RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
  118. {
  119. LOG_E("wdt device register failed.");
  120. ret = -RT_ERROR;
  121. }
  122. else
  123. {
  124. LOG_D("wdt device register success.");
  125. }
  126. return ret;
  127. }
  128. INIT_PREV_EXPORT(rt_hw_wdt_init);
  129. #endif