drv_wdt.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  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-3-25 klcheng 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. /*-------------------------------------------------------------------------------*/
  19. /* watchdog timer timeout look up table */
  20. /*-------------------------------------------------------------------------------*/
  21. /* clock = LIRC 10KHz. */
  22. /* */
  23. /* working hz toutsel exp cycles timeout (s) */
  24. /* 10000 0 4 16 0.0016 */
  25. /* 1 6 64 0.0064 */
  26. /* 2 8 256 0.0256 */
  27. /* 3 10 1024 0.1024 */
  28. /* 4 12 4096 0.4096 */
  29. /* 5 14 16384 1.6384 */
  30. /* 6 16 65536 6.5536 */
  31. /* 7 18 262144 26.2144 */
  32. /*-------------------------------------------------------------------------------*/
  33. /* clock = LXT 32768Hz. */
  34. /* */
  35. /* working hz toutsel exp cycles timeout (s) */
  36. /* 32768 0 4 16 0.0005 */
  37. /* 1 6 64 0.0020 */
  38. /* 2 8 256 0.0078 */
  39. /* 3 10 1024 0.0313 */
  40. /* 4 12 4096 0.1250 */
  41. /* 5 14 16384 0.5000 */
  42. /* 6 16 65536 2.0000 */
  43. /* 7 18 262144 8.0000 */
  44. /*-------------------------------------------------------------------------------*/
  45. /* clock = 192MHz HCLK divide 2048 = 93750 Hz. */
  46. /* */
  47. /* working hz toutsel exp cycles timeout (s) */
  48. /* 93750 0 4 16 0.00017 */
  49. /* 1 6 64 0.00068 */
  50. /* 2 8 256 0.00273 */
  51. /* 3 10 1024 0.01092 */
  52. /* 4 12 4096 0.04369 */
  53. /* 5 14 16384 0.17476 */
  54. /* 6 16 65536 0.69905 */
  55. /* 7 18 262144 2.79620 */
  56. /*-------------------------------------------------------------------------------*/
  57. /* Private define ---------------------------------------------------------------*/
  58. /* Pick a suitable wdt timeout interval, it is a trade-off between the
  59. consideration of timeout accuracy and the system performance. The MIN_CYCLES
  60. parameter is a numerical value of the toutsel setting, and it must be set to
  61. a correct one which matches to the literal meaning of MIN_TOUTSEL. */
  62. #define MIN_TOUTSEL (WDT_TIMEOUT_2POW10)
  63. #define MIN_CYCLES (1024)
  64. /* Macros to convert the value between the timeout interval and the soft time iterations. */
  65. #define ROUND_TO_INTEGER(value) ((int)(((value) * 10 + 5) / 10))
  66. #define CONV_SEC_TO_IT(hz, secs) (ROUND_TO_INTEGER((float)((secs) * (hz)) / (float)(MIN_CYCLES)))
  67. #define CONV_IT_TO_SEC(hz, iterations) (ROUND_TO_INTEGER((float)((iterations) * (MIN_CYCLES)) / (float)(hz)))
  68. /* Private typedef --------------------------------------------------------------*/
  69. struct soft_time_handle
  70. {
  71. int clock_hz;
  72. int wanted_sec;
  73. int report_sec;
  74. int left_iterations;
  75. int full_iterations;
  76. rt_bool_t expired;
  77. rt_bool_t feed_dog;
  78. };
  79. typedef volatile struct soft_time_handle soft_time_handle_t;
  80. /* Private functions ------------------------------------------------------------*/
  81. static rt_err_t wdt_init(rt_watchdog_t *dev);
  82. static rt_err_t wdt_control(rt_watchdog_t *dev, int cmd, void *args);
  83. static uint32_t wdt_get_module_clock(void);
  84. static uint32_t wdt_get_working_hz(void);
  85. static void soft_time_init(soft_time_handle_t *const soft_time);
  86. static void soft_time_setup(uint32_t wanted_sec, uint32_t hz, soft_time_handle_t *const soft_time);
  87. static void soft_time_feed_dog(soft_time_handle_t *const soft_time);
  88. #if defined(RT_USING_PM)
  89. static int wdt_pm_suspend(const struct rt_device *device, rt_uint8_t mode);
  90. static void wdt_pm_resume(const struct rt_device *device, rt_uint8_t mode);
  91. static int wdt_pm_frequency_change(const struct rt_device *device, rt_uint8_t mode);
  92. static void soft_time_freqeucy_change(uint32_t new_hz, soft_time_handle_t *const soft_time);
  93. #endif
  94. /* Public functions -------------------------------------------------------------*/
  95. /* Private variables ------------------------------------------------------------*/
  96. static struct soft_time_handle soft_time;
  97. static struct rt_watchdog_device device_wdt;
  98. static struct rt_watchdog_ops ops_wdt =
  99. {
  100. .init = wdt_init,
  101. .control = wdt_control,
  102. };
  103. #if defined(RT_USING_PM)
  104. static struct rt_device_pm_ops device_pm_ops =
  105. {
  106. .suspend = wdt_pm_suspend,
  107. .resume = wdt_pm_resume,
  108. .frequency_change = wdt_pm_frequency_change
  109. };
  110. #endif
  111. #if defined(RT_USING_PM)
  112. /* device pm suspend() entry. */
  113. static int wdt_pm_suspend(const struct rt_device *device, rt_uint8_t mode)
  114. {
  115. switch (mode)
  116. {
  117. case PM_SLEEP_MODE_NONE:
  118. case PM_SLEEP_MODE_IDLE:
  119. case PM_SLEEP_MODE_STANDBY:
  120. case PM_SLEEP_MODE_SHUTDOWN:
  121. break;
  122. case PM_SLEEP_MODE_LIGHT:
  123. case PM_SLEEP_MODE_DEEP:
  124. SYS_UnlockReg();
  125. WDT->CTL &= ~WDT_CTL_WDTEN_Msk;
  126. SYS_LockReg();
  127. break;
  128. default:
  129. break;
  130. }
  131. return (int)RT_EOK;
  132. }
  133. /* device pm resume() entry. */
  134. static void wdt_pm_resume(const struct rt_device *device, rt_uint8_t mode)
  135. {
  136. switch (mode)
  137. {
  138. case PM_SLEEP_MODE_NONE:
  139. case PM_SLEEP_MODE_IDLE:
  140. case PM_SLEEP_MODE_STANDBY:
  141. case PM_SLEEP_MODE_SHUTDOWN:
  142. break;
  143. case PM_SLEEP_MODE_LIGHT:
  144. case PM_SLEEP_MODE_DEEP:
  145. SYS_UnlockReg();
  146. WDT->CTL |= WDT_CTL_WDTEN_Msk;
  147. SYS_LockReg();
  148. break;
  149. default:
  150. break;
  151. }
  152. }
  153. /* device pm frequency_change() entry. */
  154. static int wdt_pm_frequency_change(const struct rt_device *device, rt_uint8_t mode)
  155. {
  156. uint32_t clk, new_hz;
  157. new_hz = wdt_get_working_hz();
  158. clk = wdt_get_module_clock();
  159. if (clk == CLK_CLKSEL1_WDTSEL_HCLK_DIV2048)
  160. {
  161. if (new_hz == soft_time.clock_hz)
  162. return (int)(RT_EOK);
  163. /* frequency change occurs in critical section */
  164. soft_time_freqeucy_change(new_hz, &soft_time);
  165. }
  166. return (int)(RT_EOK);
  167. }
  168. static void soft_time_freqeucy_change(uint32_t new_hz, soft_time_handle_t *const soft_time)
  169. {
  170. rt_base_t level;
  171. soft_time_handle_t new_time;
  172. rt_bool_t corner_case = RT_FALSE;
  173. level = rt_hw_interrupt_disable();
  174. new_time.clock_hz = new_hz;
  175. new_time.feed_dog = soft_time->feed_dog;
  176. new_time.expired = soft_time->expired;
  177. new_time.wanted_sec = soft_time->wanted_sec;
  178. new_time.full_iterations = CONV_SEC_TO_IT(new_hz, soft_time->wanted_sec);
  179. new_time.report_sec = CONV_IT_TO_SEC(new_hz, new_time.full_iterations);
  180. new_time.left_iterations = ROUND_TO_INTEGER((float)soft_time->left_iterations *
  181. (float)new_hz / (float)soft_time->clock_hz);
  182. if ((new_time.left_iterations == 0) && (soft_time->left_iterations > 0))
  183. {
  184. new_time.left_iterations++;;
  185. corner_case = RT_TRUE;
  186. }
  187. *soft_time = new_time;
  188. rt_hw_interrupt_enable(level);
  189. if (corner_case)
  190. {
  191. LOG_W("pm frequency change cause wdt internal left iterations convert to 0.\n\r \
  192. wdt driver will add another 1 iteration for this corner case.");
  193. }
  194. }
  195. #endif
  196. static void hw_wdt_init(void)
  197. {
  198. SYS_UnlockReg();
  199. if (WDT_GET_RESET_FLAG())
  200. {
  201. LOG_W("System re-boots from watchdog timer reset.\n");
  202. WDT_CLEAR_RESET_FLAG();
  203. }
  204. SYS_LockReg();
  205. NVIC_EnableIRQ(WDT_IRQn);
  206. }
  207. /* wdt device driver initialize. */
  208. int rt_hw_wdt_init(void)
  209. {
  210. rt_err_t ret;
  211. hw_wdt_init();
  212. device_wdt.ops = &ops_wdt;
  213. ret = rt_hw_watchdog_register(&device_wdt, "wdt", RT_DEVICE_FLAG_RDWR, RT_NULL);
  214. #if defined(RT_USING_PM)
  215. rt_pm_device_register((struct rt_device *)&device_wdt, &device_pm_ops);
  216. #endif
  217. return (int)ret;
  218. }
  219. INIT_BOARD_EXPORT(rt_hw_wdt_init);
  220. /* Register rt-thread device.init() entry. */
  221. static rt_err_t wdt_init(rt_watchdog_t *dev)
  222. {
  223. soft_time_init(&soft_time);
  224. hw_wdt_init();
  225. return RT_EOK;
  226. }
  227. static uint32_t wdt_get_module_clock(void)
  228. {
  229. return (CLK_GetModuleClockSource(WDT_MODULE) << CLK_CLKSEL1_WDTSEL_Pos);
  230. }
  231. static uint32_t wdt_get_working_hz(void)
  232. {
  233. uint32_t clk, hz = 0;
  234. clk = wdt_get_module_clock();
  235. switch (clk)
  236. {
  237. case CLK_CLKSEL1_WDTSEL_LIRC:
  238. hz = __LIRC;
  239. break;
  240. case CLK_CLKSEL1_WDTSEL_LXT:
  241. hz = __LXT;
  242. break;
  243. case CLK_CLKSEL1_WDTSEL_HCLK_DIV2048:
  244. hz = CLK_GetHCLKFreq() / 2048;
  245. break;
  246. default:
  247. break;
  248. }
  249. return hz;
  250. }
  251. static void soft_time_init(soft_time_handle_t *const soft_time)
  252. {
  253. rt_memset((void *)soft_time, 0, sizeof(struct soft_time_handle));
  254. }
  255. static void soft_time_setup(uint32_t wanted_sec, uint32_t hz, soft_time_handle_t *const soft_time)
  256. {
  257. rt_base_t level;
  258. level = rt_hw_interrupt_disable();
  259. soft_time->expired = RT_FALSE;
  260. soft_time->feed_dog = RT_FALSE;
  261. soft_time->wanted_sec = wanted_sec;
  262. soft_time->full_iterations = CONV_SEC_TO_IT(hz, wanted_sec);
  263. soft_time->left_iterations = soft_time->full_iterations;
  264. soft_time->report_sec = CONV_IT_TO_SEC(hz, soft_time->full_iterations);
  265. soft_time->clock_hz = hz;
  266. rt_hw_interrupt_enable(level);
  267. }
  268. static void soft_time_feed_dog(soft_time_handle_t *const soft_time)
  269. {
  270. soft_time->feed_dog = RT_TRUE;
  271. }
  272. /* Register rt-thread device.control() entry. */
  273. static rt_err_t wdt_control(rt_watchdog_t *dev, int cmd, void *args)
  274. {
  275. uint32_t wanted_sec, hz;
  276. uint32_t *buf;
  277. rt_err_t ret = RT_EOK;
  278. if (dev == NULL)
  279. return -(RT_EINVAL);
  280. SYS_UnlockReg();
  281. hz = wdt_get_working_hz();
  282. switch (cmd)
  283. {
  284. case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
  285. if (args == RT_NULL)
  286. {
  287. ret = RT_EINVAL;
  288. break;
  289. }
  290. buf = (uint32_t *)args;
  291. *buf = soft_time.report_sec;
  292. break;
  293. case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
  294. wanted_sec = *((uint32_t *)args);
  295. if (wanted_sec == 0)
  296. {
  297. ret = RT_EINVAL;
  298. break;
  299. }
  300. soft_time_setup(wanted_sec, hz, &soft_time);
  301. break;
  302. case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
  303. if (args == RT_NULL)
  304. {
  305. ret = RT_EINVAL;
  306. break;
  307. }
  308. buf = (uint32_t *)args;
  309. *buf = CONV_IT_TO_SEC(hz, soft_time.left_iterations);
  310. break;
  311. case RT_DEVICE_CTRL_WDT_KEEPALIVE:
  312. /* Make a mark that the application has fed the watchdog. */
  313. soft_time_feed_dog(&soft_time);
  314. break;
  315. case RT_DEVICE_CTRL_WDT_START:
  316. WDT_RESET_COUNTER();
  317. WDT_Open(MIN_TOUTSEL, WDT_RESET_DELAY_1026CLK, TRUE, TRUE);
  318. WDT_EnableInt();
  319. break;
  320. case RT_DEVICE_CTRL_WDT_STOP:
  321. WDT_Close();
  322. break;
  323. default:
  324. ret = RT_ERROR;
  325. }
  326. SYS_LockReg();
  327. return -(ret);
  328. }
  329. /* wdt interrupt entry */
  330. void WDT_IRQHandler(void)
  331. {
  332. rt_interrupt_enter();
  333. /* Clear wdt interrupt flag */
  334. if (WDT_GET_TIMEOUT_INT_FLAG())
  335. {
  336. WDT_CLEAR_TIMEOUT_INT_FLAG();
  337. }
  338. /* Clear wdt wakeup flag */
  339. if (WDT_GET_TIMEOUT_WAKEUP_FLAG())
  340. {
  341. WDT_CLEAR_TIMEOUT_WAKEUP_FLAG();
  342. }
  343. /* The soft time has not reached the configured timeout yet. Clear the wdt counter
  344. any way to prevent the system from hardware wdt reset. */
  345. if (soft_time.left_iterations-- > 0)
  346. {
  347. WDT_RESET_COUNTER();
  348. }
  349. /* The soft time reaches the configured timeout boundary. Clear the wdt
  350. counter if he application has fed the dog at least once until now. */
  351. else
  352. {
  353. if ((soft_time.feed_dog) && (!soft_time.expired))
  354. {
  355. WDT_RESET_COUNTER();
  356. soft_time.feed_dog = RT_FALSE;
  357. soft_time.left_iterations = soft_time.full_iterations;
  358. }
  359. else
  360. {
  361. /* Application does not feed the dog in time. */
  362. soft_time.expired = RT_TRUE;
  363. }
  364. }
  365. rt_interrupt_leave();
  366. }
  367. #endif /* BSP_USING_WDT */