drv_wdt.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /**************************************************************************//**
  2. *
  3. * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0
  6. *
  7. * Change Logs:
  8. * Date Author Notes
  9. * 2020-12-12 Wayne First version
  10. *
  11. ******************************************************************************/
  12. #include <rtconfig.h>
  13. #if defined(BSP_USING_WDT)
  14. #include <rthw.h>
  15. #include <rtdevice.h>
  16. #include <rtdbg.h>
  17. #include "NuMicro.h"
  18. #include <drv_sys.h>
  19. /* Private define ---------------------------------------------------------------*/
  20. /* Pick a suitable wdt timeout interval, it is a trade-off between the
  21. consideration of timeout accuracy and the system performance. The MIN_CYCLES
  22. parameter is a numerical value of the toutsel setting, and it must be set to
  23. a correct one which matches to the literal meaning of MIN_TOUTSEL. */
  24. #define MIN_TOUTSEL (WDT_TIMEOUT_2POW10)
  25. #define MIN_CYCLES (1024)
  26. /* Macros to convert the value between the timeout interval and the soft time iterations. */
  27. #define ROUND_TO_INTEGER(value) ((int)(((value) * 10 + 5) / 10))
  28. #define CONV_SEC_TO_IT(hz, secs) (ROUND_TO_INTEGER((float)((secs) * (hz)) / (float)(MIN_CYCLES)))
  29. #define CONV_IT_TO_SEC(hz, iterations) (ROUND_TO_INTEGER((float)((iterations) * (MIN_CYCLES)) / (float)(hz)))
  30. /* Private typedef --------------------------------------------------------------*/
  31. struct soft_time_handle
  32. {
  33. int clock_hz;
  34. int wanted_sec;
  35. int report_sec;
  36. int left_iterations;
  37. int full_iterations;
  38. rt_bool_t expired;
  39. rt_bool_t feed_dog;
  40. };
  41. typedef volatile struct soft_time_handle soft_time_handle_t;
  42. /* Private functions ------------------------------------------------------------*/
  43. static rt_err_t wdt_init(rt_watchdog_t *dev);
  44. static rt_err_t wdt_control(rt_watchdog_t *dev, int cmd, void *args);
  45. static uint32_t wdt_get_working_hz(void);
  46. static void soft_time_init(soft_time_handle_t *const soft_time);
  47. static void soft_time_setup(uint32_t wanted_sec, uint32_t hz, soft_time_handle_t *const soft_time);
  48. static void soft_time_feed_dog(soft_time_handle_t *const soft_time);
  49. static void nu_wdt_isr(int vector, void *param);
  50. /* Public functions -------------------------------------------------------------*/
  51. /* Private variables ------------------------------------------------------------*/
  52. static struct soft_time_handle soft_time;
  53. static struct rt_watchdog_device device_wdt;
  54. static struct rt_watchdog_ops ops_wdt =
  55. {
  56. .init = wdt_init,
  57. .control = wdt_control,
  58. };
  59. static void hw_wdt_init(void)
  60. {
  61. nu_sys_ipclk_enable(WDTCKEN);
  62. rt_hw_interrupt_install(IRQ_WDT, nu_wdt_isr, &device_wdt, "wdt");
  63. rt_hw_interrupt_umask(IRQ_WDT);
  64. }
  65. /* wdt device driver initialize. */
  66. int rt_hw_wdt_init(void)
  67. {
  68. rt_err_t ret;
  69. hw_wdt_init();
  70. device_wdt.ops = &ops_wdt;
  71. ret = rt_hw_watchdog_register(&device_wdt, "wdt", RT_DEVICE_FLAG_RDWR, RT_NULL);
  72. return (int)ret;
  73. }
  74. INIT_BOARD_EXPORT(rt_hw_wdt_init);
  75. /* Register rt-thread device.init() entry. */
  76. static rt_err_t wdt_init(rt_watchdog_t *dev)
  77. {
  78. soft_time_init(&soft_time);
  79. return RT_EOK;
  80. }
  81. static uint32_t wdt_get_working_hz(void)
  82. {
  83. static uint32_t u32ClkTbl[4] = {12000000, 12000000 / 128, 0, 32768};
  84. uint32_t u32WDT_S = (inpw(REG_CLK_DIVCTL8) & (0x3 << 8)) >> 8;
  85. uint32_t clk = 0;
  86. switch (u32WDT_S)
  87. {
  88. case 0: // XIN Hz
  89. case 1: // XIN/128 Hz
  90. case 3: // 32.768 Hz
  91. clk = u32ClkTbl[u32WDT_S];
  92. break;
  93. case 2: // PCLK/4096 Hz
  94. clk = sysGetClock(SYS_PCLK) * 1000000 / 4096;
  95. break;
  96. default:
  97. break;
  98. }
  99. return clk;
  100. }
  101. static void soft_time_init(soft_time_handle_t *const soft_time)
  102. {
  103. rt_memset((void *)soft_time, 0, sizeof(struct soft_time_handle));
  104. }
  105. static void soft_time_setup(uint32_t wanted_sec, uint32_t hz, soft_time_handle_t *const soft_time)
  106. {
  107. rt_base_t level = rt_hw_interrupt_disable();
  108. soft_time->expired = RT_FALSE;
  109. soft_time->feed_dog = RT_FALSE;
  110. soft_time->wanted_sec = wanted_sec;
  111. soft_time->full_iterations = CONV_SEC_TO_IT(hz, wanted_sec);
  112. soft_time->left_iterations = soft_time->full_iterations;
  113. soft_time->report_sec = CONV_IT_TO_SEC(hz, soft_time->full_iterations);
  114. soft_time->clock_hz = hz;
  115. rt_hw_interrupt_enable(level);
  116. }
  117. static void soft_time_feed_dog(soft_time_handle_t *const soft_time)
  118. {
  119. soft_time->feed_dog = RT_TRUE;
  120. }
  121. /* Register rt-thread device.control() entry. */
  122. static rt_err_t wdt_control(rt_watchdog_t *dev, int cmd, void *args)
  123. {
  124. uint32_t wanted_sec, hz;
  125. uint32_t *buf;
  126. rt_err_t ret = RT_EOK;
  127. if (dev == NULL)
  128. return -(RT_EINVAL);
  129. hz = wdt_get_working_hz();
  130. SYS_UnlockReg();
  131. switch (cmd)
  132. {
  133. case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
  134. if (args == RT_NULL)
  135. {
  136. ret = RT_EINVAL;
  137. break;
  138. }
  139. buf = (uint32_t *)args;
  140. *buf = soft_time.report_sec;
  141. break;
  142. case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
  143. wanted_sec = *((uint32_t *)args);
  144. if (wanted_sec == 0)
  145. {
  146. ret = RT_EINVAL;
  147. break;
  148. }
  149. soft_time_setup(wanted_sec, hz, &soft_time);
  150. break;
  151. case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
  152. if (args == RT_NULL)
  153. {
  154. ret = RT_EINVAL;
  155. break;
  156. }
  157. buf = (uint32_t *)args;
  158. *buf = CONV_IT_TO_SEC(hz, soft_time.left_iterations);
  159. break;
  160. case RT_DEVICE_CTRL_WDT_KEEPALIVE:
  161. /* Make a mark that the application has fed the watchdog. */
  162. soft_time_feed_dog(&soft_time);
  163. break;
  164. case RT_DEVICE_CTRL_WDT_START:
  165. WDT_RESET_COUNTER();
  166. WDT_Open(MIN_TOUTSEL, WDT_RESET_DELAY_1026CLK, TRUE, TRUE);
  167. WDT_EnableInt();
  168. break;
  169. case RT_DEVICE_CTRL_WDT_STOP:
  170. WDT_Close();
  171. break;
  172. default:
  173. ret = RT_ERROR;
  174. }
  175. SYS_LockReg();
  176. return -(ret);
  177. }
  178. /* wdt interrupt entry */
  179. static void nu_wdt_isr(int vector, void *param)
  180. {
  181. /* Clear wdt interrupt flag */
  182. if (WDT_GET_TIMEOUT_INT_FLAG())
  183. {
  184. WDT_CLEAR_TIMEOUT_INT_FLAG();
  185. }
  186. SYS_UnlockReg();
  187. /* The soft time has not reached the configured timeout yet. Clear the wdt counter
  188. any way to prevent the system from hardware wdt reset. */
  189. if (soft_time.left_iterations-- > 0)
  190. {
  191. WDT_RESET_COUNTER();
  192. }
  193. /* The soft time reaches the configured timeout boundary. Clear the wdt
  194. counter if he application has fed the dog at least once until now. */
  195. else
  196. {
  197. if ((soft_time.feed_dog) && (!soft_time.expired))
  198. {
  199. WDT_RESET_COUNTER();
  200. soft_time.feed_dog = RT_FALSE;
  201. soft_time.left_iterations = soft_time.full_iterations;
  202. }
  203. else
  204. {
  205. /* Application does not feed the dog in time. */
  206. soft_time.expired = RT_TRUE;
  207. }
  208. }
  209. SYS_LockReg();
  210. }
  211. #endif /* BSP_USING_WDT */