1 #include <aos/list.h>
2 #include <drv/wdt.h>
3
4 #include <devicevfs/devicevfs.h>
5 #include <drivers/ddkc_log.h>
6 #include "wdg_core.h"
7
8 // suppose there's only one watchdog exist in the system
9 wdg_controller_t g_system_wdg;
10
11
wdt_event_cb_fun(csi_wdt_t * wdt,void * arg)12 static void wdt_event_cb_fun(csi_wdt_t *wdt, void *arg)
13 {
14 wdg_controller_t *wdg = arg;
15
16 ddkc_err("watchdog%d timeout happens\r\n", wdg->id);
17
18 return;
19 }
20
21
22 /**
23 * start a new watchdog
24 *
25 * @param[in] id watchdog controller's id
26 * @param[in] ms watchdog's timeout value, in unit of ms
27 *
28 * @return handle of the watchdog device, used for later watchdog operations, when open operation success;
29 NULL when open operation fails
30 */
aos_wdg_open(uint32_t id,uint32_t ms)31 wdg_dev_handle_t aos_wdg_open (uint32_t id, uint32_t ms) {
32 aos_status_t status = 0;
33 csi_error_t ret = CSI_OK;
34 wdg_controller_t *wdg = NULL;
35 wdg_dev_t *user = NULL;
36
37 // if id is not 0, print warning message and force assign it to 0
38 if (id != 0) {
39 ddkc_warn("id:%d is not valid, force set to 0\r\n", id);
40 id = 0;
41 }
42
43 // if ms is less than 20, print warning message and force assign it to 20
44 if (ms < 20) {
45 ddkc_warn("too smaller timeout value, force set to 20ms\r\n", ms);
46 ms = 20;
47 }
48
49 wdg = &g_system_wdg;
50
51 status = aos_mutex_lock(&wdg->lock, AOS_WAIT_FOREVER);
52 if (status) {
53 ddkc_warn("lock wdg%d failed, status:%d\r\n", wdg->id, status);
54 return NULL;
55 }
56
57 // check and make sure watchdog controller is initialized
58 if (!wdg->init) {
59 ret = csi_wdt_init(&wdg->csi_dev, wdg->id);
60 if (ret != CSI_OK) {
61 ddkc_err("wdt%d init failed, ret:%d\r\n", wdg->id, ret);
62 goto err;
63 }
64 csi_wdt_attach_callback(&wdg->csi_dev, wdt_event_cb_fun, wdg);
65 wdg->init = true;
66 }
67
68 user = malloc(sizeof(wdg_dev_t));
69 if (!user) {
70 ddkc_err("malloc for wdg_dev_t failed\r\n");
71 goto err;
72 }
73 user->id = id;
74 user->controller = wdg;
75 dlist_init(&user->node);
76 user->wdg_dev_handle = AOS_WDG_MAGIC;
77 user->started = false;
78 user->timeout = ms;
79
80 ret = aos_mutex_new(&user->lock);
81 if (ret) {
82 ddkc_assert(1, "aos_mutex_new for watchdog user lock failed, ret:%d\r\n", ret);
83 free(user);
84 user = NULL;
85
86 goto err;
87 }
88
89 dlist_add(&user->node, &wdg->user);
90
91 status = aos_mutex_unlock(&wdg->lock);
92 if (status) {
93 ddkc_warn("unlock wdg%d failed, status:%d\r\n", wdg->id, status);
94 }
95
96 return &user->wdg_dev_handle;
97
98 err:
99 status = aos_mutex_unlock(&wdg->lock);
100 if (status) {
101 ddkc_warn("unlock wdg%d failed, status:%d\r\n", wdg->id, status);
102 }
103
104 return NULL;
105 }
106
_aos_wdg_auto_config(wdg_controller_t * wdg)107 aos_status_t _aos_wdg_auto_config(wdg_controller_t *wdg)
108 {
109 int32_t ret = 0;
110 aos_status_t status = 0;
111 bool b = false;
112 uint32_t tmo = 0xffffffff;
113 dlist_t *head = &wdg->user;
114 dlist_t *pos = NULL, *n = NULL;
115 wdg_dev_t *w = NULL;
116
117 // go through watchdog's user list and pick one with shortest tmo setting from the started users
118 dlist_for_each_safe(pos, n, head)
119 {
120 w = aos_container_of(pos, wdg_dev_t, node);
121 if (w->started && tmo > w->timeout)
122 {
123 tmo = w->timeout;
124 b = true;
125 }
126 }
127 if (b) {
128 // reconfig watchdog's timeout value
129 /// TODO: need to check the return value
130 csi_wdt_stop(&wdg->csi_dev);
131 ret += csi_wdt_set_timeout(&wdg->csi_dev, tmo);
132 ret += csi_wdt_start(&wdg->csi_dev);
133 } else {
134 // stop the watchdog
135 /// TODO: need to check the return value
136 csi_wdt_stop(&wdg->csi_dev);
137 wdg->started = false;
138 }
139
140 if (ret) {
141 ddkc_assert(1, "reconfig watchdog controller failed, ret:%d\r\n", ret);
142 }
143
144 return ret;
145 }
146
147 /**
148 * close target watchdog handle
149 *
150 * @param[in] dev handle of the watchdog device to be operated, must be the return value of aos_wdg_open
151 *
152 * @return 0 if operation success
153 negative error code if operation fails
154 */
aos_wdg_close(wdg_dev_handle_t dev_h)155 aos_status_t aos_wdg_close (wdg_dev_handle_t dev_h) {
156 int ret = 0;
157 aos_status_t status = 0;
158 wdg_dev_t *user = NULL;
159 wdg_controller_t *wdg = NULL;
160
161 if (!dev_h || (*dev_h != AOS_WDG_MAGIC)) {
162 ddkc_err("invalid dev_h:%p, *dev_h:0x%x", dev_h, dev_h ? *dev_h : 0);
163 return -EINVAL;
164 }
165
166 user = aos_container_of(dev_h, wdg_dev_t, wdg_dev_handle);
167 wdg = (wdg_controller_t *)(user->controller);
168
169 status = aos_mutex_lock(&wdg->lock, AOS_WAIT_FOREVER);
170 if (status) {
171 /// TODO: how to handle this case? cannot recover from this case for the moment
172 ddkc_assert(1, "lock wdg%d failed, status:%d\r\n", wdg->id, status);
173 return -EIO;
174 }
175
176 dlist_del(&user->node);
177 if (dlist_empty(&wdg->user)) {
178 // stop watchdog if it is started
179 csi_wdt_stop(&wdg->csi_dev);
180
181 csi_wdt_detach_callback(&wdg->csi_dev);
182
183 csi_wdt_uninit(&wdg->csi_dev);
184 wdg->tmo = 0;
185 wdg->init = false;
186 } else {
187 // check whether tmo equals to current watchdog or not, reset tmo to shortst in the user list
188 if (wdg->tmo == user->timeout)
189 {
190 _aos_wdg_auto_config(wdg);
191 }
192 }
193
194 status = aos_mutex_unlock(&wdg->lock);
195 if (status) {
196 ddkc_warn("unlock wdg%d failed, status:%d\r\n", wdg->id, status);
197 }
198
199 return 0;
200 }
201
202 /**
203 * start/stop target watchdog
204 *
205 * @param[in] dev handle of the watchdog device to be operated, must be the return value of aos_wdg_open
206 *
207 * @return 0 if operation success
208 negative error code if operation fails
209 */
aos_wdg_control(wdg_dev_handle_t dev_h,bool enable)210 aos_status_t aos_wdg_control (wdg_dev_handle_t dev_h, bool enable) {
211 int ret = 0;
212 aos_status_t status = 0;
213 wdg_dev_t *user = NULL;
214 wdg_controller_t *wdg = NULL;
215
216 if (!dev_h || (*dev_h != AOS_WDG_MAGIC)) {
217 ddkc_err("invalid dev_h:%p, *dev_h:0x%x", dev_h, dev_h ? *dev_h : 0);
218 return -EINVAL;
219 }
220
221 user = aos_container_of(dev_h, wdg_dev_t, wdg_dev_handle);
222 wdg = (wdg_controller_t *)(user->controller);
223
224 status = aos_mutex_lock(&wdg->lock, AOS_WAIT_FOREVER);
225 if (status) {
226 ddkc_warn("lock wdg%d failed, status:%d\r\n", wdg->id, status);
227 return -EIO;
228 }
229 user->started = enable;
230 // if current watchdog user's tmo is shorter than watchdog controller's setting, reconfig the watchdog
231 // if current watchdog user's is disabled, reconfig the watchdog
232 if (((wdg->tmo > user->timeout) && (enable)) || (!enable))
233 {
234 _aos_wdg_auto_config(wdg);
235 }
236
237 status = aos_mutex_unlock(&wdg->lock);
238 if (status) {
239 ddkc_warn("unlock wdg%d failed, status:%d\r\n", wdg->id, status);
240 }
241 return 0;
242 }
243
244
245 /**
246 * start target watchdog
247 *
248 * @param[in] dev handle of the watchdog device to be operated, must be the return value of aos_wdg_open
249 *
250 * @return 0 if operation success
251 negative error code if operation fails
252 */
aos_wdg_start(wdg_dev_handle_t dev_h)253 aos_status_t aos_wdg_start (wdg_dev_handle_t dev_h) {
254 return aos_wdg_control(dev_h, true);
255 }
256
257 /**
258 * stop target watchdog
259 *
260 * @param[in] dev handle of the watchdog device to be operated, must be the return value of aos_wdg_open
261 *
262 * @return 0 if operation success
263 negative error code if operation fails
264 */
aos_wdg_stop(wdg_dev_handle_t dev_h)265 aos_status_t aos_wdg_stop (wdg_dev_handle_t dev_h) {
266 return aos_wdg_control(dev_h, false);
267 }
268
269 /**
270 * set timeout value of target watchdog
271 *
272 * @param[in] dev handle of the watchdog device to be operated, must be the return value of aos_wdg_open
273 * @param[in] clk watchdog's new timeout value, in unit of ms
274 *
275 * @return 0 if operation success
276 negative error code if operation fails
277 */
aos_wdg_timeout_set(wdg_dev_handle_t dev_h,uint32_t ms)278 aos_status_t aos_wdg_timeout_set (wdg_dev_handle_t dev_h, uint32_t ms) {
279 aos_status_t status = 0;
280 wdg_dev_t *user = NULL;
281
282 if (!dev_h || (*dev_h != AOS_WDG_MAGIC)) {
283 ddkc_err("invalid dev_h:%p, *dev_h:0x%x", dev_h, dev_h ? *dev_h : 0);
284 return -EINVAL;
285 }
286
287 user = aos_container_of(dev_h, wdg_dev_t, wdg_dev_handle);
288
289 /// TODO: shall protect this procedure
290 user->timeout = ms;
291
292 if (user->started)
293 status = aos_wdg_control(dev_h, true);
294
295 return status;
296 }
297
298 /**
299 * feed target watchdog
300 *
301 * @param[in] dev handle of the watchdog device to be operated, must be the return value of aos_wdg_open
302 *
303 * @return 0 if operation success
304 negative error code if operation fails
305 */
aos_wdg_feed(wdg_dev_handle_t dev_h)306 aos_status_t aos_wdg_feed (wdg_dev_handle_t dev_h) {
307 int ret = 0;
308 aos_status_t status = 0;
309 wdg_dev_t *user = NULL;
310 wdg_controller_t *wdg = NULL;
311
312 if (!dev_h || (*dev_h != AOS_WDG_MAGIC)) {
313 ddkc_err("invalid dev_h:%p, *dev_h:0x%x", dev_h, dev_h ? *dev_h : 0);
314 return -EINVAL;
315 }
316
317 user = aos_container_of(dev_h, wdg_dev_t, wdg_dev_handle);
318 wdg = (wdg_controller_t *)(user->controller);
319
320 status = aos_mutex_lock(&wdg->lock, AOS_WAIT_FOREVER);
321 if (status) {
322 ddkc_warn("lock wdg%d failed, status:%d\r\n", wdg->id, status);
323 return -EIO;
324 }
325
326 ret = csi_wdt_feed(&wdg->csi_dev);
327 if (ret) {
328 ddkc_warn("feed watchdog failed, ret:%d\r\n", ret);
329 return -EIO;
330 }
331
332 status = aos_mutex_unlock(&wdg->lock);
333 if (status) {
334 ddkc_warn("unlock wdg%d failed, status:%d\r\n", wdg->id, status);
335 }
336 return 0;
337 }
338
aos_wdg_get_num(void)339 uint32_t aos_wdg_get_num (void) {
340 return 1;
341 }
342
343
aos_wdg_init(void)344 int32_t aos_wdg_init (void) {
345 aos_status_t ret = 0;
346
347 // init global watchdog controller's variable
348 g_system_wdg.id = 0;
349 g_system_wdg.tmo = 0;
350 g_system_wdg.init = false;
351 INIT_AOS_DLIST_HEAD(&g_system_wdg.user);
352
353 // init global i2c controller's lock
354 ret = aos_mutex_new(&g_system_wdg.lock);
355 if (ret) {
356 ddkc_assert(1, "aos_mutex_new for system watchdog lock failed, ret:%d\r\n", ret);
357 }
358
359 ddkc_warn("enter %s\r\n", __func__);
360
361 return 0;
362 }
363
364 BUS_DRIVER_ENTRY(aos_wdg_init)
365