1 /*
2  * Copyright (c) 2006-2022, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author            Notes
8  * 2021-08-25     AisinoChip        First Version
9  */
10 
11 #include <board.h>
12 #include <rtthread.h>
13 #include <rtdevice.h>
14 
15 
16 #ifdef RT_USING_WDT
17 #include "board.h"
18 
19 struct acm32_wdt_obj
20 {
21     union
22     {
23         WDT_HandleTypeDef       wdt;
24         IWDT_HandleTypeDef      iwdt;
25     } handle;
26     rt_uint16_t             is_start;
27     rt_uint16_t             type;
28     rt_watchdog_t           watchdog;
29 };
30 
31 #define TYPE_WDT         0
32 #define TYPE_IWDT        1
33 #define IWDT_FREQ        (32000)
34 
35 #ifdef BSP_USING_WDT
36     #define WDT_NAME        "wdt"
37     static struct acm32_wdt_obj acm32_wdt;
38 #endif
39 
40 #ifdef BSP_USING_IWDT
41     #define IWDT_NAME       "iwdt"
42     static struct acm32_wdt_obj acm32_iwdt;
43 #endif
44 
45 static struct rt_watchdog_ops ops;
46 
calc_wdt_divisor_load(rt_uint32_t freq,rt_uint32_t sec,rt_uint32_t * divisor,rt_uint32_t * load)47 rt_inline rt_base_t calc_wdt_divisor_load(rt_uint32_t freq, rt_uint32_t sec, rt_uint32_t *divisor, rt_uint32_t *load)
48 {
49     rt_uint32_t freqMaxSec = 0;
50     rt_uint32_t minFreqDiv = WDT_DIVISOR_NONE;
51 
52     freqMaxSec = RT_UINT32_MAX / freq;
53 
54     while (minFreqDiv <= WDT_DIVISOR_128)
55     {
56         if (sec < freqMaxSec)
57         {
58             break;
59         }
60         minFreqDiv ++;
61         freqMaxSec = RT_UINT32_MAX / freq * (1 << minFreqDiv);
62     }
63 
64     if (minFreqDiv > WDT_DIVISOR_128)
65     {
66         return -1;
67     }
68 
69     *divisor = minFreqDiv;
70     *load = sec * (freq >> minFreqDiv);
71     return 0;
72 }
73 
calc_iwdt_divisor_load(rt_uint32_t freq,rt_uint32_t sec,rt_uint32_t * divisor,rt_uint32_t * load)74 rt_inline rt_base_t calc_iwdt_divisor_load(rt_uint32_t freq, rt_uint32_t sec, rt_uint32_t *divisor, rt_uint32_t *load)
75 {
76     rt_uint32_t minFreqDiv = IWDT_CLOCK_PRESCALER_4;
77     rt_uint32_t freqMaxMs = 0;
78 
79     freqMaxMs = IWDT_RELOAD_MAX_VALUE * 1000 / (freq >> (2 + minFreqDiv));
80 
81     while (minFreqDiv <= IWDT_CLOCK_PRESCALER_256)
82     {
83         if (sec * 1000 < freqMaxMs)
84         {
85             break;
86         }
87         minFreqDiv ++;
88         freqMaxMs = IWDT_RELOAD_MAX_VALUE * 1000 / (freq >> (2 + minFreqDiv));
89     }
90 
91     if (minFreqDiv > IWDT_CLOCK_PRESCALER_256)
92     {
93         return -1;
94     }
95 
96     *divisor = minFreqDiv;
97     if (sec < 1000)
98     {
99         *load = (sec * 1000) * IWDT_RELOAD_MAX_VALUE / freqMaxMs;
100     }
101     else
102     {
103         *load = (sec) * IWDT_RELOAD_MAX_VALUE / freqMaxMs / 1000;
104     }
105     return 0;
106 }
107 
calc_wdt_timeout(rt_uint32_t freq,rt_uint32_t divisor,rt_uint32_t count)108 rt_inline rt_uint32_t calc_wdt_timeout(rt_uint32_t freq, rt_uint32_t divisor, rt_uint32_t count)
109 {
110     /* 1 / ( freq / (1<<divisor) ) * (count) */
111     return (rt_uint32_t)(((rt_uint64_t)count) * (1 << divisor) / (freq));
112 }
113 
calc_iwdt_timeout(rt_uint32_t freq,rt_uint32_t divisor,rt_uint32_t count)114 rt_inline rt_uint32_t calc_iwdt_timeout(rt_uint32_t freq, rt_uint32_t divisor, rt_uint32_t count)
115 {
116     /* (freq >> (2+divisor)) / IWDT_RELOAD_MAX_VALUE * count */
117     return count / (freq >> (2 + divisor));
118 }
119 
wdt_init(rt_watchdog_t * wdt)120 static rt_err_t wdt_init(rt_watchdog_t *wdt)
121 {
122     return RT_EOK;
123 }
124 
wdt_control(rt_watchdog_t * wdt,int cmd,void * arg)125 static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
126 {
127     struct acm32_wdt_obj *wdtObj = NULL;
128     rt_uint32_t timer_clk_hz;
129     rt_uint32_t divisor, load;
130 
131     RT_ASSERT(wdt != RT_NULL);
132 
133     wdtObj = rt_container_of(wdt, struct acm32_wdt_obj, watchdog);
134     timer_clk_hz = System_Get_APBClock();
135 
136     switch (cmd)
137     {
138     /* feed the watchdog */
139     case RT_DEVICE_CTRL_WDT_KEEPALIVE:
140         if (TYPE_WDT == wdtObj->type)
141         {
142             HAL_WDT_Feed(&wdtObj->handle.wdt);
143         }
144         else
145         {
146             HAL_IWDT_Kick_Watchdog_Wait_For_Done(&wdtObj->handle.iwdt);
147         }
148         break;
149     /* set watchdog timeout, seconds */
150     case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
151         if (TYPE_WDT == wdtObj->type)
152         {
153             if (calc_wdt_divisor_load(timer_clk_hz, (*((rt_uint32_t *)arg)), &divisor, &load))
154             {
155                 return -RT_ERROR;
156             }
157             wdtObj->handle.wdt.Init.WDTDivisor = (WDT_DIVISOR)divisor;
158             wdtObj->handle.wdt.Init.WDTLoad = load;
159             HAL_WDT_Init(&wdtObj->handle.wdt);
160         }
161         else
162         {
163             if (calc_iwdt_divisor_load(IWDT_FREQ, (*((rt_uint32_t *)arg)), &divisor, &load))
164             {
165                 return -RT_ERROR;
166             }
167             wdtObj->handle.iwdt.Instance = IWDT;
168             wdtObj->handle.iwdt.Init.Prescaler = divisor;
169             wdtObj->handle.iwdt.Init.Reload = load;
170         }
171 
172         if (wdtObj->is_start)
173         {
174             if (TYPE_WDT == wdtObj->type)
175             {
176                 HAL_WDT_Init(&wdtObj->handle.wdt);
177             }
178             else
179             {
180                 HAL_IWDT_Init(&wdtObj->handle.iwdt);
181             }
182         }
183         break;
184     case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
185         if (TYPE_WDT == wdtObj->type)
186         {
187             (*((rt_uint32_t *)arg)) = calc_wdt_timeout(timer_clk_hz,
188                                       wdtObj->handle.wdt.Init.WDTDivisor,
189                                       wdtObj->handle.wdt.Instance->COUNT);
190         }
191         else
192         {
193             return -RT_EINVAL;
194         }
195         break;
196     case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
197         if (TYPE_WDT == wdtObj->type)
198         {
199             (*((rt_uint32_t *)arg)) = calc_wdt_timeout(timer_clk_hz,
200                                       wdtObj->handle.wdt.Init.WDTDivisor,
201                                       wdtObj->handle.wdt.Init.WDTLoad);
202         }
203         else
204         {
205             (*((rt_uint32_t *)arg)) = calc_iwdt_timeout(IWDT_FREQ,
206                                       wdtObj->handle.iwdt.Init.Prescaler,
207                                       wdtObj->handle.iwdt.Init.Reload);
208         }
209         break;
210     case RT_DEVICE_CTRL_WDT_START:
211         if (TYPE_WDT == wdtObj->type)
212         {
213             wdtObj->handle.wdt.Instance = WDT;
214             wdtObj->handle.wdt.Init.WDTMode = WDT_MODE_RST;
215             wdtObj->handle.wdt.Init.WDTINTCLRTIME = 0xffff;
216             HAL_WDT_Init(&wdtObj->handle.wdt);
217             HAL_WDT_Start(&wdtObj->handle.wdt);
218         }
219         else
220         {
221             wdtObj->handle.iwdt.Instance->CMDR = IWDT_ENABLE_COMMAND;
222             wdtObj->handle.iwdt.Init.Window = IWDT_RELOAD_MAX_VALUE;  /* window function disabled when window >= reload */
223             wdtObj->handle.iwdt.Init.Wakeup = IWDT_RELOAD_MAX_VALUE;  /* wakeup function disabled when wakeup >= reload */
224             HAL_IWDT_Init(&wdtObj->handle.iwdt);
225         }
226         wdtObj->is_start = 1;
227         break;
228     case RT_DEVICE_CTRL_WDT_STOP:
229         if (TYPE_WDT == wdtObj->type)
230         {
231             HAL_WDT_Stop(&wdtObj->handle.wdt);
232         }
233         else
234         {
235             wdtObj->handle.iwdt.Instance->CMDR = IWDT_DISABLE_COMMAND;
236         }
237         wdtObj->is_start = 0;
238         break;
239     default:
240         return -RT_ERROR;
241     }
242     return RT_EOK;
243 }
244 
rt_wdt_init(void)245 int rt_wdt_init(void)
246 {
247     ops.init = &wdt_init;
248     ops.control = &wdt_control;
249 
250 #ifdef BSP_USING_WDT
251     acm32_wdt.type = TYPE_WDT;
252     acm32_wdt.is_start = 0;
253     acm32_wdt.watchdog.ops = &ops;
254     if (rt_hw_watchdog_register(&acm32_wdt.watchdog, WDT_NAME, RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
255     {
256         return -RT_ERROR;
257     }
258 #endif
259 #ifdef BSP_USING_IWDT
260     acm32_iwdt.type = TYPE_IWDT;
261     acm32_iwdt.is_start = 0;
262     acm32_iwdt.watchdog.ops = &ops;
263     if (rt_hw_watchdog_register(&acm32_iwdt.watchdog, IWDT_NAME, RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
264     {
265         return -RT_ERROR;
266     }
267 #endif
268 
269     return RT_EOK;
270 }
271 INIT_BOARD_EXPORT(rt_wdt_init);
272 
273 #endif /* RT_USING_WDT */
274 
275