1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author            Notes
8  * 2022-03-04     stevetong459      first version
9  * 2022-12-26     luobeihai         add APM32F0 series MCU support
10  * 2023-03-28     luobeihai         add APM32E1/S1 series MCU support
11  */
12 
13 #include <board.h>
14 #include <sys/time.h>
15 
16 #ifdef RT_USING_WDT
17 
18 #define DBG_TAG             "drv.wdt"
19 #define DBG_LVL             DBG_INFO
20 #include <rtdbg.h>
21 
22 #ifndef LSI_VALUE
23     #define LSI_VALUE            ((uint32_t)40000)
24 #endif
25 
26 #define DRV_WDT_TIME_OUT      0xFFFF
27 
28 typedef struct
29 {
30     struct rt_watchdog_device wdt;
31     rt_uint32_t min_threshold;
32     rt_uint32_t max_threshold;
33     rt_uint32_t current_threshold;
34 } apm32_wdt_t;
35 
36 static apm32_wdt_t wdt_config;
37 
apm32_iwdt_init(rt_watchdog_t * wdt)38 static rt_err_t apm32_iwdt_init(rt_watchdog_t *wdt)
39 {
40     rt_uint32_t counter = 0;
41 
42     RCM_EnableLSI();
43 
44     while (!RCM_ReadStatusFlag(RCM_FLAG_LSIRDY))
45     {
46         if (++counter > DRV_WDT_TIME_OUT)
47         {
48             LOG_E("LSI clock open failed.");
49             return -RT_ERROR;
50         }
51     }
52 
53     wdt_config.min_threshold = 1;
54     wdt_config.max_threshold = (0xfff << 8) / LSI_VALUE;
55     LOG_I("threshold section [%u, %d]", \
56           wdt_config.min_threshold,
57           wdt_config.max_threshold);
58 
59 #if defined(SOC_SERIES_APM32F0)
60     while (IWDT_ReadStatusFlag(IWDT_FLAG_DIVU))
61 #elif defined(SOC_SERIES_APM32F1) || defined(SOC_SERIES_APM32E1) || defined(SOC_SERIES_APM32S1) \
62     || defined(SOC_SERIES_APM32F4)
63     while (IWDT_ReadStatusFlag(IWDT_FLAG_PSCU))
64 #endif
65     {
66         if (++counter > DRV_WDT_TIME_OUT)
67         {
68             LOG_E("watchdog prescaler init failed.");
69             return -RT_ERROR;
70         }
71     }
72 
73     IWDT_EnableWriteAccess();
74 
75 #if defined(SOC_SERIES_APM32F0)
76     IWDT_ConfigDivider(IWDT_DIV_256);
77 #elif defined(SOC_SERIES_APM32F1) || defined(SOC_SERIES_APM32E1) || defined(SOC_SERIES_APM32S1) \
78     || defined(SOC_SERIES_APM32F4)
79     IWDT_ConfigDivider(IWDT_DIVIDER_256);
80 #endif
81 
82     IWDT_DisableWriteAccess();
83 
84     return RT_EOK;
85 }
86 
87 /**
88  * @brief    This function will control watchdog device.
89  *
90  * @param    wdt is a pointer to i2c config class.
91  *
92  * @return   RT_EOK indicates successful , other value indicates failed.
93  */
apm32_iwdt_control(rt_watchdog_t * wdt,int cmd,void * arg)94 static rt_err_t apm32_iwdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
95 {
96     volatile rt_uint32_t param, counter = 0;
97 
98     switch (cmd)
99     {
100     case RT_DEVICE_CTRL_WDT_KEEPALIVE:
101         IWDT_Refresh();
102         break;
103     case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
104         param = *(rt_uint32_t *) arg;
105         if ((param > wdt_config.max_threshold) || \
106                 (param < wdt_config.min_threshold))
107         {
108             LOG_E("invalid param@%u.", param);
109             return -RT_ERROR;
110         }
111         else
112         {
113             wdt_config.current_threshold = param;
114         }
115         while (IWDT_ReadStatusFlag(IWDT_FLAG_CNTU))
116         {
117             if (++counter > DRV_WDT_TIME_OUT)
118             {
119                 LOG_E("Update watchdog reload value complete.");
120                 return -RT_ERROR;
121             }
122         }
123         IWDT_Refresh();
124         IWDT_EnableWriteAccess();
125         IWDT_ConfigReload(param * LSI_VALUE >> 8);
126         IWDT_DisableWriteAccess();
127         break;
128     case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
129         *(rt_uint32_t *)arg = wdt_config.current_threshold;
130         break;
131     case RT_DEVICE_CTRL_WDT_START:
132         IWDT_Enable();
133         IWDT_Refresh();
134         break;
135     default:
136         LOG_W("This command is not supported.");
137         return -RT_ERROR;
138     }
139 
140     return RT_EOK;
141 }
142 
143 static struct rt_watchdog_ops apm32_wdt_ops =
144 {
145     apm32_iwdt_init,
146     apm32_iwdt_control,
147 };
148 
rt_hw_wdt_init(void)149 static int rt_hw_wdt_init(void)
150 {
151     wdt_config.wdt.ops = &apm32_wdt_ops;
152     /* register watchdog device */
153     if (rt_hw_watchdog_register(&wdt_config.wdt, "wdt", \
154                                 RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
155     {
156         LOG_E("wdt device register failed.");
157         return -RT_ERROR;
158     }
159     LOG_D("wdt device register success.");
160 
161     return RT_EOK;
162 }
163 INIT_BOARD_EXPORT(rt_hw_wdt_init);
164 
165 #endif
166