wdt.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*
  2. * This file is part of FH8620 BSP for RT-Thread distribution.
  3. *
  4. * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd.
  5. * All rights reserved
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. *
  21. * Visit http://www.fullhan.com to get contact with Fullhan.
  22. *
  23. * Change Logs:
  24. * Date Author Notes
  25. */
  26. #include "fh_def.h"
  27. #include "wdt.h"
  28. #include "interrupt.h"
  29. #include "board_info.h"
  30. #include "inc/fh_driverlib.h"
  31. #include <rtdevice.h>
  32. #include <drivers/watchdog.h>
  33. #define FH_WDT_DEBUG
  34. #ifdef FH_WDT_DEBUG
  35. #define PRINT_WDT_DBG(fmt, args...) \
  36. do \
  37. { \
  38. rt_kprintf("FH_WDT_DEBUG: "); \
  39. rt_kprintf(fmt, ## args); \
  40. } \
  41. while(0)
  42. #else
  43. #define PRINT_WDT_DBG(fmt, args...) do { } while (0)
  44. #endif
  45. static int heartbeat = WDT_HEARTBEAT;
  46. static rt_uint32_t fh_wdt_time_left(struct wdt_driver *wdt_drv)
  47. {
  48. struct fh_wdt_obj *wdt_obj = wdt_drv->priv;
  49. //todo: get clk
  50. //return WDT_GetCurrCount(wdt_obj) / WDT_CLOCK;
  51. return WDT_GetCurrCount(wdt_obj) / 1800000;
  52. }
  53. static void fh_wdt_keepalive(struct wdt_driver *wdt_drv)
  54. {
  55. struct fh_wdt_obj *wdt_obj = wdt_drv->priv;
  56. WDT_Kick(wdt_obj);
  57. }
  58. static inline void fh_wdt_set_next_heartbeat(struct wdt_driver *wdt_drv)
  59. {
  60. wdt_drv->next_heartbeat = rt_tick_get() + heartbeat * RT_TICK_PER_SECOND;
  61. }
  62. static inline int fh_wdt_top_in_seconds(struct wdt_driver *wdt_drv, unsigned top)
  63. {
  64. /*
  65. * There are 16 possible timeout values in 0..15 where the number of
  66. * cycles is 2 ^ (16 + i) and the watchdog counts down.
  67. */
  68. //todo: get_clk
  69. return (1 << (17 + top)) / 1800000;
  70. }
  71. static int fh_wdt_set_top(struct wdt_driver *wdt_drv, unsigned top_s)
  72. {
  73. int i, top_val = FH_WDT_MAX_TOP;
  74. struct fh_wdt_obj *wdt_obj = wdt_drv->priv;
  75. /*
  76. * Iterate over the timeout values until we find the closest match. We
  77. * always look for >=.
  78. */
  79. for (i = 0; i <= FH_WDT_MAX_TOP; ++i)
  80. if (fh_wdt_top_in_seconds(wdt_drv, i) >= top_s) {
  81. top_val = i;
  82. break;
  83. }
  84. /* Set the new value in the watchdog. */
  85. PRINT_WDT_DBG("[wdt] set topval: %d\n", top_val);
  86. WDT_SetTopValue(wdt_obj, top_val);
  87. fh_wdt_set_next_heartbeat(wdt_drv);
  88. return fh_wdt_top_in_seconds(wdt_drv, top_val);
  89. }
  90. rt_err_t fh_watchdog_init(rt_watchdog_t *wdt)
  91. {
  92. struct wdt_driver *wdt_drv = wdt->parent.user_data;
  93. struct fh_wdt_obj *wdt_obj = wdt_drv->priv;
  94. if (wdt_drv->in_use)
  95. return -RT_EBUSY;
  96. //todo: spinlock
  97. fh_wdt_set_top(wdt_drv, WDT_HW_TIMEOUT);///3000);
  98. if (!WDT_IsEnable(wdt_obj))
  99. {
  100. /*
  101. * The watchdog is not currently enabled. Set the timeout to
  102. * the maximum and then start it.
  103. */
  104. rt_uint32_t value;
  105. value = WDOG_CONTROL_REG_WDT_EN_MASK;
  106. value |= WDOG_CONTROL_REG_RMOD_MASK;
  107. WDT_SetCtrl(wdt_obj, value);
  108. fh_wdt_keepalive(wdt_drv);
  109. }
  110. fh_wdt_set_next_heartbeat(wdt_drv);
  111. //todo: unlock
  112. return RT_EOK;
  113. }
  114. rt_err_t fh_watchdog_ctrl(rt_watchdog_t *wdt, int cmd, void *arg)
  115. {
  116. struct wdt_driver *wdt_drv = wdt->parent.user_data;
  117. struct fh_wdt_obj *wdt_obj = wdt_drv->priv;
  118. rt_uint32_t val;
  119. switch (cmd)
  120. {
  121. case RT_DEVICE_CTRL_WDT_START:
  122. WDT_Enable(wdt_obj, RT_TRUE);
  123. break;
  124. case RT_DEVICE_CTRL_WDT_STOP:
  125. WDT_Enable(wdt_obj, RT_FALSE);
  126. break;
  127. case RT_DEVICE_CTRL_WDT_KEEPALIVE:
  128. //fh_wdt_set_next_heartbeat(wdt_drv);
  129. fh_wdt_keepalive(wdt_drv);
  130. break;
  131. case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
  132. heartbeat = *((int*)(arg));
  133. PRINT_WDT_DBG("[wdt] settime value %lu\n", heartbeat);
  134. fh_wdt_set_top(wdt_drv, heartbeat);///3000);
  135. fh_wdt_keepalive(wdt_drv);
  136. fh_wdt_set_next_heartbeat(wdt_drv);
  137. break;
  138. case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
  139. arg = &heartbeat;
  140. break;
  141. case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
  142. val = fh_wdt_time_left(wdt_drv);
  143. arg = &val;
  144. break;
  145. default:
  146. return -RT_EIO;
  147. }
  148. return RT_EOK;
  149. }
  150. static void fh_wdt_interrupt(int irq, void *param)
  151. {
  152. //todo: stop
  153. //fh81_pmu_stop();
  154. }
  155. struct rt_watchdog_ops fh_watchdog_ops =
  156. {
  157. .init = &fh_watchdog_init,
  158. .control = &fh_watchdog_ctrl,
  159. };
  160. int fh_wdt_probe(void *priv_data)
  161. {
  162. rt_watchdog_t *wdt_dev;
  163. struct wdt_driver *wdt_drv;
  164. struct fh_wdt_obj *wdt_obj = (struct fh_wdt_obj *)priv_data;
  165. wdt_drv = (struct wdt_driver *)rt_malloc(sizeof(struct wdt_driver));
  166. rt_memset(wdt_drv, 0, sizeof(struct wdt_driver));
  167. wdt_drv->priv = wdt_obj;
  168. rt_hw_interrupt_install(wdt_obj->irq, fh_wdt_interrupt, (void *)wdt_drv, "wdt_irq");
  169. rt_hw_interrupt_umask(wdt_obj->irq);
  170. //todo: clk
  171. wdt_dev = (rt_watchdog_t *)rt_malloc(sizeof(rt_watchdog_t));
  172. if (wdt_dev == RT_NULL)
  173. {
  174. rt_kprintf("ERROR: %s rt_watchdog_t malloc failed\n", __func__);
  175. }
  176. wdt_dev->ops = &fh_watchdog_ops;
  177. rt_hw_watchdog_register(wdt_dev, "fh_wdt", RT_DEVICE_OFLAG_RDWR, wdt_drv);
  178. return 0;
  179. }
  180. int fh_wdt_exit(void *priv_data)
  181. {
  182. return 0;
  183. }
  184. struct fh_board_ops wdt_driver_ops =
  185. {
  186. .probe = fh_wdt_probe,
  187. .exit = fh_wdt_exit,
  188. };
  189. void rt_hw_wdt_init(void)
  190. {
  191. PRINT_WDT_DBG("%s start\n", __func__);
  192. fh_board_driver_register("wdt", &wdt_driver_ops);
  193. PRINT_WDT_DBG("%s end\n", __func__);
  194. }
  195. void wdt_start(int timeout, int kick_times)
  196. {
  197. rt_device_t wdt_dev;
  198. int ret;
  199. wdt_dev = rt_device_find("fh_wdt");
  200. rt_device_open(wdt_dev, 0);
  201. ret = rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);
  202. int i = 0;
  203. for( ; i < kick_times; i ++ )
  204. {
  205. rt_thread_sleep(timeout * RT_TICK_PER_SECOND / 2);
  206. ret = rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, &timeout);
  207. rt_kprintf( "kicked\n" );
  208. }
  209. rt_kprintf( "stop kick the watchdog, it shall reboot for %d seconds.\n", timeout * 2);
  210. }
  211. #ifdef RT_USING_WDT
  212. #ifdef RT_USING_FINSH
  213. #include <finsh.h>
  214. FINSH_FUNCTION_EXPORT(wdt_start, enable wdt);
  215. #endif
  216. #endif