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