drv_wdt.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /*
  2. * Copyright (C) 2022-2024, Xiaohua Semiconductor Co., Ltd.
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2023-02-08 CDT first version
  9. * 2023-12-01 CDT added swdt support
  10. */
  11. #include "board.h"
  12. #ifdef BSP_USING_WDT_TMR
  13. #include <math.h>
  14. #include <string.h>
  15. // #define DRV_DEBUG
  16. #define LOG_TAG "drv_wdt"
  17. #include <drv_log.h>
  18. enum
  19. {
  20. WDT_INIT_ING,
  21. WDT_INIT_OVER,
  22. WDT_IS_ENABLE
  23. };
  24. static struct rt_watchdog_ops _ops;
  25. /* WDT */
  26. #ifdef BSP_USING_WDT
  27. struct hc32_wdt_obj
  28. {
  29. rt_watchdog_t watchdog;
  30. stc_wdt_init_t stcwdg;
  31. rt_uint32_t pclk3;
  32. rt_uint8_t sta;
  33. rt_uint8_t index;
  34. };
  35. static struct hc32_wdt_obj hc32_wdt;
  36. struct time_match
  37. {
  38. uint32_t u32ClockDiv;
  39. uint32_t u32CountPeriod;
  40. float timeout_s;
  41. };
  42. static uint32_t const Div[] = {4U, 64U, 128U, 256U, 512U, 1024U, 2048U, 8192U};
  43. static uint32_t const Peri[] = {256U, 4096U, 16384U, 65536U};
  44. static struct time_match wdt_match[(sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))];
  45. static void wdt_match_init(uint32_t clock)
  46. {
  47. int i, j;
  48. for (i = 0; i < (sizeof(Div) / sizeof(Div[0])); i++)
  49. {
  50. for (j = 0; j < (sizeof(Peri) / sizeof(Peri[0])); j++)
  51. {
  52. wdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].u32ClockDiv = Div[i];
  53. wdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].u32CountPeriod = Peri[j];
  54. wdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].timeout_s = (Div[i] * Peri[j]) / (float)clock;
  55. }
  56. }
  57. }
  58. static void wdt_match_sort(void)
  59. {
  60. int i, j;
  61. struct time_match Temp;
  62. /* bubble sort */
  63. for (i = 0; i < ((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0])) - 1); i++)
  64. {
  65. for (j = 0; j < ((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0])) - i - 1); j++)
  66. {
  67. if (wdt_match[j].timeout_s > wdt_match[j + 1].timeout_s)
  68. {
  69. memcpy(&Temp, &wdt_match[j], sizeof(struct time_match));
  70. memcpy(&wdt_match[j], &wdt_match[j + 1], sizeof(struct time_match));
  71. memcpy(&wdt_match[j + 1], &Temp, sizeof(struct time_match));
  72. }
  73. }
  74. }
  75. }
  76. static int wdt_match_find_index(uint32_t time_out)
  77. {
  78. int i;
  79. /* Min and Max case */
  80. if (time_out <= wdt_match[0].timeout_s)
  81. {
  82. return 0;
  83. }
  84. else if (time_out >= wdt_match[((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1].timeout_s)
  85. {
  86. return (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1);
  87. }
  88. /* Other case */
  89. for (i = 1; i < (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1); i++)
  90. {
  91. if (time_out >= wdt_match[i].timeout_s && time_out < wdt_match[i + 1].timeout_s)
  92. {
  93. /* Min difference */
  94. if (time_out - wdt_match[i].timeout_s < wdt_match[i + 1].timeout_s - time_out)
  95. {
  96. return i;
  97. }
  98. else
  99. {
  100. return (i + 1);
  101. }
  102. }
  103. }
  104. /* Not match case */
  105. return (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1);
  106. }
  107. static rt_uint32_t wdt_match_find_period(rt_uint32_t Period)
  108. {
  109. rt_uint32_t CountPeriod = 0U;
  110. switch (Period)
  111. {
  112. case 256U:
  113. CountPeriod = WDT_CNT_PERIOD256;
  114. break;
  115. case 4096U:
  116. CountPeriod = WDT_CNT_PERIOD4096;
  117. break;
  118. case 16384U:
  119. CountPeriod = WDT_CNT_PERIOD16384;
  120. break;
  121. case 65536U:
  122. CountPeriod = WDT_CNT_PERIOD65536;
  123. break;
  124. default:
  125. break;
  126. }
  127. return CountPeriod;
  128. }
  129. static rt_uint32_t wdt_get_timeout_s(void)
  130. {
  131. /* timeout(s) = PERI * DIV / PCLK3 */
  132. return ((rt_uint32_t)(wdt_match[hc32_wdt.index].u32CountPeriod * wdt_match[hc32_wdt.index].u32ClockDiv / (float)hc32_wdt.pclk3));
  133. }
  134. static rt_uint32_t wdt_get_timeleft_s(void)
  135. {
  136. /* wdt is down counter */
  137. return ((rt_uint32_t)(WDT_GetCountValue() * wdt_match[hc32_wdt.index].u32ClockDiv / (float)hc32_wdt.pclk3));
  138. }
  139. static rt_err_t _wdt_init(rt_watchdog_t *wdt)
  140. {
  141. hc32_wdt.pclk3 = CLK_GetBusClockFreq(CLK_BUS_PCLK3);
  142. LOG_D("pclk3 = %dhz", hc32_wdt.pclk3);
  143. if (!hc32_wdt.pclk3)
  144. {
  145. LOG_E("pclk3 getbusclockfreq failed.");
  146. return -RT_ERROR;
  147. }
  148. wdt_match_init(hc32_wdt.pclk3);
  149. wdt_match_sort();
  150. hc32_wdt.stcwdg.u32RefreshRange = WDT_RANGE_0TO100PCT;
  151. #ifdef BSP_WDT_CONTINUE_COUNT
  152. hc32_wdt.stcwdg.u32LPMCount = WDT_LPM_CNT_CONT;
  153. #else
  154. hc32_wdt.stcwdg.u32LPMCount = WDT_LPM_CNT_STOP;
  155. #endif
  156. hc32_wdt.stcwdg.u32ExceptionType = WDT_EXP_TYPE_RST;
  157. hc32_wdt.sta = WDT_INIT_ING;
  158. /* WDT_CR register only support write once,so can't call WDT_Init of ther */
  159. return RT_EOK;
  160. }
  161. static rt_err_t _wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
  162. {
  163. switch (cmd)
  164. {
  165. /* feed the watchdog */
  166. case RT_DEVICE_CTRL_WDT_KEEPALIVE:
  167. /* Prevention of unexpected start-up when feed dog */
  168. if (hc32_wdt.sta == WDT_IS_ENABLE)
  169. {
  170. WDT_FeedDog();
  171. }
  172. break;
  173. /* set watchdog timeout */
  174. case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
  175. hc32_wdt.index = wdt_match_find_index((*((rt_uint32_t *)arg)));
  176. hc32_wdt.stcwdg.u32CountPeriod = wdt_match_find_period(wdt_match[hc32_wdt.index].u32CountPeriod);
  177. hc32_wdt.stcwdg.u32ClockDiv = ((uint32_t)log2(wdt_match[hc32_wdt.index].u32ClockDiv) << WDT_CR_CKS_POS);
  178. if (WDT_Init(&hc32_wdt.stcwdg) != LL_OK)
  179. {
  180. LOG_E("wdg set timeout failed.");
  181. return -RT_ERROR;
  182. }
  183. hc32_wdt.sta = WDT_INIT_OVER;
  184. LOG_D("wdg set timeout successful. timeout = %d s", wdt_get_timeout_s());
  185. break;
  186. case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
  187. (*((rt_uint32_t *)arg)) = wdt_get_timeout_s();
  188. break;
  189. case RT_DEVICE_CTRL_WDT_START:
  190. if (hc32_wdt.sta == WDT_INIT_ING)
  191. {
  192. LOG_E("please set the timeout values.");
  193. return -RT_ERROR;
  194. }
  195. /* First reload counter to start WDT */
  196. WDT_FeedDog();
  197. hc32_wdt.sta = WDT_IS_ENABLE;
  198. break;
  199. case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
  200. (*((rt_uint32_t *)arg)) = wdt_get_timeleft_s();
  201. break;
  202. default:
  203. LOG_W("This command is not supported.");
  204. return -RT_ERROR;
  205. }
  206. return RT_EOK;
  207. }
  208. int rt_wdt_init(void)
  209. {
  210. _ops.init = &_wdt_init;
  211. _ops.control = &_wdt_control;
  212. hc32_wdt.watchdog.ops = &_ops;
  213. /* register watchdog device */
  214. if (rt_hw_watchdog_register(&hc32_wdt.watchdog, "wdt", RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
  215. {
  216. LOG_E("wdt device register failed.");
  217. return -RT_ERROR;
  218. }
  219. LOG_D("wdt device register success.");
  220. return RT_EOK;
  221. }
  222. INIT_BOARD_EXPORT(rt_wdt_init);
  223. /* SWDT */
  224. #else /* BSP_USING_SWDT */
  225. struct hc32_swdt_obj
  226. {
  227. rt_watchdog_t watchdog;
  228. stc_swdt_init_t stcwdg;
  229. rt_uint32_t swdtclk;
  230. rt_uint8_t sta;
  231. rt_uint8_t index;
  232. };
  233. static struct hc32_swdt_obj hc32_swdt;
  234. struct time_match
  235. {
  236. uint32_t u32ClockDiv;
  237. uint32_t u32CountPeriod;
  238. float timeout_s;
  239. };
  240. static uint32_t const Div[] = {1U, 16U, 32U, 64U, 128U, 256U, 2048U};
  241. static uint32_t const Peri[] = {256U, 4096U, 16384U, 65536U};
  242. static struct time_match swdt_match[(sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))];
  243. static void swdt_match_init(uint32_t clock)
  244. {
  245. int i, j;
  246. for (i = 0; i < (sizeof(Div) / sizeof(Div[0])); i++)
  247. {
  248. for (j = 0; j < (sizeof(Peri) / sizeof(Peri[0])); j++)
  249. {
  250. swdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].u32ClockDiv = Div[i];
  251. swdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].u32CountPeriod = Peri[j];
  252. swdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].timeout_s = (Div[i] * Peri[j]) / (float)clock;
  253. }
  254. }
  255. }
  256. static void swdt_match_sort(void)
  257. {
  258. int i, j;
  259. struct time_match Temp;
  260. /* bubble sort */
  261. for (i = 0; i < ((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0])) - 1); i++)
  262. {
  263. for (j = 0; j < ((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0])) - i - 1); j++)
  264. {
  265. if (swdt_match[j].timeout_s > swdt_match[j + 1].timeout_s)
  266. {
  267. memcpy(&Temp, &swdt_match[j], sizeof(struct time_match));
  268. memcpy(&swdt_match[j], &swdt_match[j + 1], sizeof(struct time_match));
  269. memcpy(&swdt_match[j + 1], &Temp, sizeof(struct time_match));
  270. }
  271. }
  272. }
  273. }
  274. static int swdt_match_find_index(uint32_t time_out)
  275. {
  276. int i;
  277. /* Min and Max case */
  278. if (time_out <= swdt_match[0].timeout_s)
  279. {
  280. return 0;
  281. }
  282. else if (time_out >= swdt_match[((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1].timeout_s)
  283. {
  284. return (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1);
  285. }
  286. /* Other case */
  287. for (i = 1; i < (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1); i++)
  288. {
  289. if (time_out >= swdt_match[i].timeout_s && time_out < swdt_match[i + 1].timeout_s)
  290. {
  291. /* Min difference */
  292. if (time_out - swdt_match[i].timeout_s < swdt_match[i + 1].timeout_s - time_out)
  293. {
  294. return i;
  295. }
  296. else
  297. {
  298. return (i + 1);
  299. }
  300. }
  301. }
  302. /* Not match case */
  303. return (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1);
  304. }
  305. static rt_uint32_t swdt_match_find_period(rt_uint32_t Period)
  306. {
  307. rt_uint32_t CountPeriod = 0U;
  308. switch (Period)
  309. {
  310. case 256U:
  311. CountPeriod = SWDT_CNT_PERIOD256;
  312. break;
  313. case 4096U:
  314. CountPeriod = SWDT_CNT_PERIOD4096;
  315. break;
  316. case 16384U:
  317. CountPeriod = SWDT_CNT_PERIOD16384;
  318. break;
  319. case 65536U:
  320. CountPeriod = SWDT_CNT_PERIOD65536;
  321. break;
  322. default:
  323. break;
  324. }
  325. return CountPeriod;
  326. }
  327. static rt_uint32_t swdt_get_timeout_s(void)
  328. {
  329. /* timeout(s) = PERI * DIV / SWDTCLK */
  330. return ((rt_uint32_t)(swdt_match[hc32_swdt.index].u32CountPeriod * swdt_match[hc32_swdt.index].u32ClockDiv / (float)hc32_swdt.swdtclk));
  331. }
  332. static rt_uint32_t swdt_get_timeleft_s(void)
  333. {
  334. /* swdt is down counter */
  335. return ((rt_uint32_t)(SWDT_GetCountValue() * swdt_match[hc32_swdt.index].u32ClockDiv / (float)hc32_swdt.swdtclk));
  336. }
  337. static rt_err_t swdt_init(rt_watchdog_t *swdt)
  338. {
  339. hc32_swdt.swdtclk = 10000U;
  340. swdt_match_init(hc32_swdt.swdtclk);
  341. swdt_match_sort();
  342. hc32_swdt.stcwdg.u32RefreshRange = SWDT_RANGE_0TO100PCT;
  343. #ifdef BSP_WDT_CONTINUE_COUNT
  344. hc32_swdt.stcwdg.u32LPMCount = SWDT_LPM_CNT_CONT;
  345. #else
  346. hc32_swdt.stcwdg.u32LPMCount = SWDT_LPM_CNT_STOP;
  347. #endif
  348. hc32_swdt.stcwdg.u32ExceptionType = SWDT_EXP_TYPE_RST;
  349. hc32_swdt.sta = WDT_INIT_ING;
  350. /* SWDT_CR register only support write once,so can't call swdt_Init of ther */
  351. return RT_EOK;
  352. }
  353. static rt_err_t swdt_control(rt_watchdog_t *swdt, int cmd, void *arg)
  354. {
  355. switch (cmd)
  356. {
  357. /* feed the watchdog */
  358. case RT_DEVICE_CTRL_WDT_KEEPALIVE:
  359. /* Prevention of unexpected start-up when feed dog */
  360. if (hc32_swdt.sta == WDT_IS_ENABLE)
  361. {
  362. SWDT_FeedDog();
  363. }
  364. break;
  365. /* set watchdog timeout */
  366. case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
  367. hc32_swdt.index = swdt_match_find_index((*((rt_uint32_t *)arg)));
  368. hc32_swdt.stcwdg.u32CountPeriod = swdt_match_find_period(swdt_match[hc32_swdt.index].u32CountPeriod);
  369. hc32_swdt.stcwdg.u32ClockDiv = ((uint32_t)log2(swdt_match[hc32_swdt.index].u32ClockDiv) << SWDT_CR_CKS_POS);
  370. if (SWDT_Init(&hc32_swdt.stcwdg) != LL_OK)
  371. {
  372. LOG_E("swdg set timeout failed.");
  373. return -RT_ERROR;
  374. }
  375. hc32_swdt.sta = WDT_INIT_OVER;
  376. LOG_D("swdg set timeout successful. timeout = %d s", swdt_get_timeout_s());
  377. break;
  378. case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
  379. (*((rt_uint32_t *)arg)) = swdt_get_timeout_s();
  380. break;
  381. case RT_DEVICE_CTRL_WDT_START:
  382. if (hc32_swdt.sta == WDT_INIT_ING)
  383. {
  384. LOG_E("please set the timeout values.");
  385. return -RT_ERROR;
  386. }
  387. /* First reload counter to start swdt */
  388. SWDT_FeedDog();
  389. hc32_swdt.sta = WDT_IS_ENABLE;
  390. break;
  391. case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
  392. (*((rt_uint32_t *)arg)) = swdt_get_timeleft_s();
  393. break;
  394. default:
  395. LOG_W("This command is not supported.");
  396. return -RT_ERROR;
  397. }
  398. return RT_EOK;
  399. }
  400. static int rt_hw_swdt_init(void)
  401. {
  402. _ops.init = &swdt_init;
  403. _ops.control = &swdt_control;
  404. hc32_swdt.watchdog.ops = &_ops;
  405. /* register watchdog device */
  406. if (rt_hw_watchdog_register(&hc32_swdt.watchdog, "swdt", RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
  407. {
  408. LOG_E("swdt device register failed.");
  409. return -RT_ERROR;
  410. }
  411. LOG_D("swdt device register success.");
  412. return RT_EOK;
  413. }
  414. INIT_BOARD_EXPORT(rt_hw_swdt_init);
  415. #endif /* BSP_USING_SWDT */
  416. #endif