drv_wdt.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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. #if defined(RT_USING_PM)
  51. static int wdt_pm_suspend(const struct rt_device *device, rt_uint8_t mode);
  52. static void wdt_pm_resume(const struct rt_device *device, rt_uint8_t mode);
  53. static int wdt_pm_frequency_change(const struct rt_device *device, rt_uint8_t mode);
  54. static void soft_time_freqeucy_change(uint32_t new_hz, soft_time_handle_t *const soft_time);
  55. #endif
  56. /* Public functions -------------------------------------------------------------*/
  57. /* Private variables ------------------------------------------------------------*/
  58. static struct soft_time_handle soft_time;
  59. static struct rt_watchdog_device device_wdt;
  60. static struct rt_watchdog_ops ops_wdt =
  61. {
  62. .init = wdt_init,
  63. .control = wdt_control,
  64. };
  65. #if defined(RT_USING_PM)
  66. static struct rt_device_pm_ops device_pm_ops =
  67. {
  68. .suspend = wdt_pm_suspend,
  69. .resume = wdt_pm_resume,
  70. .frequency_change = wdt_pm_frequency_change
  71. };
  72. #endif
  73. #if defined(RT_USING_PM)
  74. /* device pm suspend() entry. */
  75. static int wdt_pm_suspend(const struct rt_device *device, rt_uint8_t mode)
  76. {
  77. switch (mode)
  78. {
  79. case PM_SLEEP_MODE_NONE:
  80. case PM_SLEEP_MODE_IDLE:
  81. case PM_SLEEP_MODE_STANDBY:
  82. case PM_SLEEP_MODE_SHUTDOWN:
  83. break;
  84. case PM_SLEEP_MODE_LIGHT:
  85. case PM_SLEEP_MODE_DEEP:
  86. outpw(REG_WDT_CTL, inpw(REG_WDT_CTL) & ~(1 << 7));
  87. break;
  88. default:
  89. break;
  90. }
  91. return (int)RT_EOK;
  92. }
  93. /* device pm resume() entry. */
  94. static void wdt_pm_resume(const struct rt_device *device, rt_uint8_t mode)
  95. {
  96. switch (mode)
  97. {
  98. case PM_SLEEP_MODE_NONE:
  99. case PM_SLEEP_MODE_IDLE:
  100. case PM_SLEEP_MODE_STANDBY:
  101. case PM_SLEEP_MODE_SHUTDOWN:
  102. break;
  103. case PM_SLEEP_MODE_LIGHT:
  104. case PM_SLEEP_MODE_DEEP:
  105. outpw(REG_WDT_CTL, inpw(REG_WDT_CTL) | (1 << 7));
  106. break;
  107. default:
  108. break;
  109. }
  110. }
  111. /* device pm frequency_change() entry. */
  112. static int wdt_pm_frequency_change(const struct rt_device *device, rt_uint8_t mode)
  113. {
  114. return (int)(RT_EOK);
  115. }
  116. static void soft_time_freqeucy_change(uint32_t new_hz, soft_time_handle_t *const soft_time)
  117. {
  118. rt_base_t level;
  119. soft_time_handle_t new_time;
  120. rt_bool_t corner_case = RT_FALSE;
  121. level = rt_hw_interrupt_disable();
  122. new_time.clock_hz = new_hz;
  123. new_time.feed_dog = soft_time->feed_dog;
  124. new_time.expired = soft_time->expired;
  125. new_time.wanted_sec = soft_time->wanted_sec;
  126. new_time.full_iterations = CONV_SEC_TO_IT(new_hz, soft_time->wanted_sec);
  127. new_time.report_sec = CONV_IT_TO_SEC(new_hz, new_time.full_iterations);
  128. new_time.left_iterations = ROUND_TO_INTEGER((float)soft_time->left_iterations *
  129. (float)new_hz / (float)soft_time->clock_hz);
  130. if ((new_time.left_iterations == 0) && (soft_time->left_iterations > 0))
  131. {
  132. new_time.left_iterations++;;
  133. corner_case = RT_TRUE;
  134. }
  135. *soft_time = new_time;
  136. rt_hw_interrupt_enable(level);
  137. if (corner_case)
  138. {
  139. LOG_W("pm frequency change cause wdt internal left iterations convert to 0.\n\r \
  140. wdt driver will add another 1 iteration for this corner case.");
  141. }
  142. }
  143. #endif
  144. static void hw_wdt_init(void)
  145. {
  146. nu_sys_ipclk_enable(WDTCKEN);
  147. SYS_UnlockReg();
  148. if (WDT_GET_RESET_FLAG())
  149. {
  150. LOG_W("System re-boots from watchdog timer reset.\n");
  151. WDT_CLEAR_RESET_FLAG();
  152. }
  153. SYS_LockReg();
  154. rt_hw_interrupt_install(IRQ_WDT, nu_wdt_isr, &device_wdt, "wdt");
  155. rt_hw_interrupt_umask(IRQ_WDT);
  156. }
  157. /* wdt device driver initialize. */
  158. int rt_hw_wdt_init(void)
  159. {
  160. rt_err_t ret;
  161. hw_wdt_init();
  162. device_wdt.ops = &ops_wdt;
  163. ret = rt_hw_watchdog_register(&device_wdt, "wdt", RT_DEVICE_FLAG_RDWR, RT_NULL);
  164. #if defined(RT_USING_PM)
  165. rt_pm_device_register((struct rt_device *)&device_wdt, &device_pm_ops);
  166. #endif
  167. return (int)ret;
  168. }
  169. INIT_BOARD_EXPORT(rt_hw_wdt_init);
  170. /* Register rt-thread device.init() entry. */
  171. static rt_err_t wdt_init(rt_watchdog_t *dev)
  172. {
  173. soft_time_init(&soft_time);
  174. return RT_EOK;
  175. }
  176. static uint32_t wdt_get_working_hz(void)
  177. {
  178. static uint32_t u32ClkTbl[4] = {12000000, 12000000 / 512, 0, 32768};
  179. uint32_t u32WDT_S = (inpw(REG_CLK_DIVCTL8) & (0x3 << 8)) >> 8;
  180. uint32_t clk = 0;
  181. switch (u32WDT_S)
  182. {
  183. case 0: // XIN Hz
  184. case 1: // XIN/512 Hz
  185. case 3: // 32.768 Hz
  186. clk = u32ClkTbl[u32WDT_S];
  187. break;
  188. case 2: // PCLK2/4096 Hz
  189. clk = sysGetClock(SYS_PCLK2) * 1000000 / 4096;
  190. break;
  191. default:
  192. break;
  193. }
  194. return clk;
  195. }
  196. static void soft_time_init(soft_time_handle_t *const soft_time)
  197. {
  198. rt_memset((void *)soft_time, 0, sizeof(struct soft_time_handle));
  199. }
  200. static void soft_time_setup(uint32_t wanted_sec, uint32_t hz, soft_time_handle_t *const soft_time)
  201. {
  202. rt_base_t level;
  203. level = rt_hw_interrupt_disable();
  204. soft_time->expired = RT_FALSE;
  205. soft_time->feed_dog = RT_FALSE;
  206. soft_time->wanted_sec = wanted_sec;
  207. soft_time->full_iterations = CONV_SEC_TO_IT(hz, wanted_sec);
  208. soft_time->left_iterations = soft_time->full_iterations;
  209. soft_time->report_sec = CONV_IT_TO_SEC(hz, soft_time->full_iterations);
  210. soft_time->clock_hz = hz;
  211. rt_hw_interrupt_enable(level);
  212. }
  213. static void soft_time_feed_dog(soft_time_handle_t *const soft_time)
  214. {
  215. soft_time->feed_dog = RT_TRUE;
  216. }
  217. /* Register rt-thread device.control() entry. */
  218. static rt_err_t wdt_control(rt_watchdog_t *dev, int cmd, void *args)
  219. {
  220. uint32_t wanted_sec, hz;
  221. uint32_t *buf;
  222. rt_err_t ret = RT_EOK;
  223. if (dev == NULL)
  224. return -(RT_EINVAL);
  225. SYS_UnlockReg();
  226. hz = wdt_get_working_hz();
  227. switch (cmd)
  228. {
  229. case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
  230. if (args == RT_NULL)
  231. {
  232. ret = RT_EINVAL;
  233. break;
  234. }
  235. buf = (uint32_t *)args;
  236. *buf = soft_time.report_sec;
  237. break;
  238. case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
  239. wanted_sec = *((uint32_t *)args);
  240. if (wanted_sec == 0)
  241. {
  242. ret = RT_EINVAL;
  243. break;
  244. }
  245. soft_time_setup(wanted_sec, hz, &soft_time);
  246. break;
  247. case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
  248. if (args == RT_NULL)
  249. {
  250. ret = RT_EINVAL;
  251. break;
  252. }
  253. buf = (uint32_t *)args;
  254. *buf = CONV_IT_TO_SEC(hz, soft_time.left_iterations);
  255. break;
  256. case RT_DEVICE_CTRL_WDT_KEEPALIVE:
  257. /* Make a mark that the application has fed the watchdog. */
  258. soft_time_feed_dog(&soft_time);
  259. break;
  260. case RT_DEVICE_CTRL_WDT_START:
  261. WDT_RESET_COUNTER();
  262. WDT_Open(MIN_TOUTSEL, WDT_RESET_DELAY_1026CLK, TRUE, TRUE);
  263. WDT_EnableInt();
  264. break;
  265. case RT_DEVICE_CTRL_WDT_STOP:
  266. WDT_Close();
  267. break;
  268. default:
  269. ret = RT_ERROR;
  270. }
  271. SYS_LockReg();
  272. return -(ret);
  273. }
  274. /* wdt interrupt entry */
  275. static void nu_wdt_isr(int vector, void *param)
  276. {
  277. /* Clear wdt interrupt flag */
  278. if (WDT_GET_TIMEOUT_INT_FLAG())
  279. {
  280. WDT_CLEAR_TIMEOUT_INT_FLAG();
  281. }
  282. /* Clear wdt wakeup flag */
  283. if (WDT_GET_TIMEOUT_WAKEUP_FLAG())
  284. {
  285. WDT_CLEAR_TIMEOUT_WAKEUP_FLAG();
  286. }
  287. /* The soft time has not reached the configured timeout yet. Clear the wdt counter
  288. any way to prevent the system from hardware wdt reset. */
  289. if (soft_time.left_iterations-- > 0)
  290. {
  291. WDT_RESET_COUNTER();
  292. }
  293. /* The soft time reaches the configured timeout boundary. Clear the wdt
  294. counter if he application has fed the dog at least once until now. */
  295. else
  296. {
  297. if ((soft_time.feed_dog) && (!soft_time.expired))
  298. {
  299. WDT_RESET_COUNTER();
  300. soft_time.feed_dog = RT_FALSE;
  301. soft_time.left_iterations = soft_time.full_iterations;
  302. }
  303. else
  304. {
  305. /* Application does not feed the dog in time. */
  306. soft_time.expired = RT_TRUE;
  307. }
  308. }
  309. }
  310. #endif /* BSP_USING_WDT */