drv_wdt.c 14 KB

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