1 /*
2 * Copyright (C) 2022-2024, Xiaohua Semiconductor Co., Ltd.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2022-04-28 CDT first version
9 * 2022-05-31 CDT delete this file
10 * 2022-06-10 xiaoxiaolisunny re-add this file for F460
11 * 2023-02-14 CDT add alarm(precision is 1 minute)
12 * 2024-06-07 CDT Add support for F448/F472
13 */
14
15 #include <board.h>
16 #include <sys/time.h>
17 #include "board_config.h"
18
19 #if defined(BSP_USING_RTC)
20
21 //#define DRV_DEBUG
22 #define LOG_TAG "drv.rtc"
23 #include <drv_log.h>
24
25 #if defined(HC32F4A0) || defined(HC32F4A8)
26 /* BACKUP REG: 96~127 for RTC used */
27 #define RTC_BACKUP_DATA_SIZE (32U)
28 #define RTC_BACKUP_REG_OFFSET (128U - RTC_BACKUP_DATA_SIZE)
29
30 static const uint8_t m_au8BackupWriteData[RTC_BACKUP_DATA_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
31 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
32 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
33 31
34 };
35 static uint8_t m_au8BackupReadData[RTC_BACKUP_DATA_SIZE];
36 #endif
37
38 static rt_rtc_dev_t rtc_dev;
39
40 #ifdef RT_USING_ALARM
41 struct stc_hc32_alarm_irq
42 {
43 struct hc32_irq_config irq_config;
44 func_ptr_t irq_callback;
45 };
46
47 static void _rtc_alarm_irq_handler(void);
48
49 #define RTC_ALARM_IRQ_CONFIG \
50 { \
51 .irq_num = BSP_RTC_ALARM_IRQ_NUM, \
52 .irq_prio = BSP_RTC_ALARM_IRQ_PRIO, \
53 .int_src = INT_SRC_RTC_ALM, \
54 }
55
56 static struct stc_hc32_alarm_irq hc32_alarm_irq =
57 {
58 .irq_config = RTC_ALARM_IRQ_CONFIG,
59 .irq_callback = _rtc_alarm_irq_handler,
60 };
61 #endif
62
63 #if defined(HC32F4A0) || defined(HC32F4A8)
_bakup_reg_write(void)64 static void _bakup_reg_write(void)
65 {
66 uint8_t u8Num;
67 for (u8Num = 0U; u8Num < RTC_BACKUP_DATA_SIZE; u8Num++)
68 {
69 PWC_BKR_Write(u8Num + RTC_BACKUP_REG_OFFSET, m_au8BackupWriteData[u8Num]);
70 }
71 }
72
_bakup_reg_check(void)73 static int32_t _bakup_reg_check(void)
74 {
75 uint8_t u8Num;
76 int32_t i32Ret = LL_OK;
77
78 for (u8Num = 0U; u8Num < RTC_BACKUP_DATA_SIZE; u8Num++)
79 {
80 m_au8BackupReadData[u8Num] = PWC_BKR_Read(u8Num + RTC_BACKUP_REG_OFFSET);
81 }
82
83 for (u8Num = 0U; u8Num < RTC_BACKUP_DATA_SIZE; u8Num++)
84 {
85 if (m_au8BackupWriteData[u8Num] != m_au8BackupReadData[u8Num])
86 {
87 i32Ret = LL_ERR;
88 break;
89 }
90 }
91
92 return i32Ret;
93 }
94
_hc32_rtc_rw_check(void)95 static int32_t _hc32_rtc_rw_check(void)
96 {
97 int32_t i32Ret = LL_ERR;
98
99 /* Enter read/write mode */
100 if (LL_OK == RTC_EnterRwMode())
101 {
102 /* Exit read/write mode */
103 if (LL_OK == RTC_ExitRwMode())
104 {
105 i32Ret = LL_OK;
106 }
107 }
108
109 return i32Ret;
110 }
111 #endif
112
_rtc_get_timeval(struct timeval * tv)113 static rt_err_t _rtc_get_timeval(struct timeval *tv)
114 {
115
116 stc_rtc_time_t stcRtcTime = {0};
117 stc_rtc_date_t stcRtcDate = {0};
118 struct tm tm_new = {0};
119
120 if (LL_OK != RTC_GetTime(RTC_DATA_FMT_DEC, &stcRtcTime))
121 {
122 return -RT_ERROR;
123 }
124 if (LL_OK != RTC_GetDate(RTC_DATA_FMT_DEC, &stcRtcDate))
125 {
126 return -RT_ERROR;
127 }
128
129 tm_new.tm_sec = stcRtcTime.u8Second;
130 tm_new.tm_min = stcRtcTime.u8Minute;
131 tm_new.tm_hour = stcRtcTime.u8Hour;
132 tm_new.tm_mday = stcRtcDate.u8Day;
133 tm_new.tm_mon = stcRtcDate.u8Month - 1;
134 tm_new.tm_year = stcRtcDate.u8Year + 100;
135
136 tv->tv_sec = timegm(&tm_new);
137
138 return RT_EOK;
139 }
140
hc32_rtc_set_time_stamp(time_t time_stamp)141 static rt_err_t hc32_rtc_set_time_stamp(time_t time_stamp)
142 {
143 stc_rtc_time_t stcRtcTime = {0};
144 stc_rtc_date_t stcRtcDate = {0};
145 struct tm tm_set = {0};
146
147 gmtime_r(&time_stamp, &tm_set);
148
149 if (tm_set.tm_year < 100)
150 {
151 return -RT_ERROR;
152 }
153
154 stcRtcTime.u8Second = tm_set.tm_sec ;
155 stcRtcTime.u8Minute = tm_set.tm_min ;
156 stcRtcTime.u8Hour = tm_set.tm_hour;
157 stcRtcDate.u8Day = tm_set.tm_mday;
158 stcRtcDate.u8Month = tm_set.tm_mon + 1;
159 stcRtcDate.u8Year = tm_set.tm_year - 100;
160 stcRtcDate.u8Weekday = tm_set.tm_wday;
161
162 if (LL_OK != RTC_SetTime(RTC_DATA_FMT_DEC, &stcRtcTime))
163 {
164 return -RT_ERROR;
165 }
166 if (LL_OK != RTC_SetDate(RTC_DATA_FMT_DEC, &stcRtcDate))
167 {
168 return -RT_ERROR;
169 }
170
171 LOG_D("set rtc time.");
172 return RT_EOK;
173 }
174
175 #if defined(HC32F4A0) || defined(HC32F460)
176 #if defined(BSP_RTC_USING_XTAL32)
177 #define RTC_CLK_SRC_SEL (RTC_CLK_SRC_XTAL32)
178 #else
179 #define RTC_CLK_SRC_SEL (RTC_CLK_SRC_LRC)
180 #endif
181 #elif defined(HC32F448) || defined(HC32F4A8)
182 #if defined(BSP_RTC_USING_XTAL32)
183 #define RTC_CLK_SRC_SEL (RTC_CLK_SRC_XTAL32)
184 #elif defined(BSP_RTC_USING_XTAL_DIV)
185 #define RTC_CLK_SRC_SEL (RTC_CLK_SRC_XTAL_DIV)
186 #else
187 #define RTC_CLK_SRC_SEL (RTC_CLK_SRC_LRC)
188 #endif
189 #elif defined(HC32F472)
190 #if defined(BSP_RTC_USING_XTAL32)
191 #define RTC_CLK_SRC_SEL (RTC_CLK_SRC_XTAL32)
192 #elif defined(BSP_RTC_USING_XTAL_DIV)
193 #define RTC_CLK_SRC_SEL (RTC_CLK_SRC_XTAL_DIV)
194 #elif defined(BSP_RTC_USING_EXTCLK)
195 #define RTC_CLK_SRC_SEL (RTC_CLK_SRC_EXTCLK)
196 #else
197 #define RTC_CLK_SRC_SEL (RTC_CLK_SRC_LRC)
198 #endif
199 #endif
200
201 #if defined(HC32F4A8)
VBAT_PowerDownCheck(void)202 static en_flag_status_t VBAT_PowerDownCheck(void)
203 {
204 en_flag_status_t ret;
205 ret = PWC_VBAT_GetStatus(PWC_FLAG_VBAT_POR);
206 if (SET == ret)
207 {
208 PWC_VBAT_ClearStatus(PWC_FLAG_VBAT_POR);
209 }
210 return ret;
211 }
212 #endif
213
_rtc_init(void)214 static rt_err_t _rtc_init(void)
215 {
216 stc_rtc_init_t stcRtcInit;
217
218 #if defined(HC32F4A8)
219 if ((SET == VBAT_PowerDownCheck()) || (LL_OK != _bakup_reg_check()) || (LL_OK != _hc32_rtc_rw_check()))
220 #elif defined(HC32F4A0)
221 if ((LL_OK != _bakup_reg_check()) || (LL_OK != _hc32_rtc_rw_check()))
222 #elif defined(HC32F460) || defined(HC32F448) || defined(HC32F472)
223 if (DISABLE == RTC_GetCounterState())
224 #endif
225 {
226 /* Reset RTC counter */
227 if (LL_ERR_TIMEOUT == RTC_DeInit())
228 {
229 LOG_E("Reset RTC failed!");
230 return -RT_ERROR;
231 }
232 else
233 {
234 /* Stop RTC */
235 RTC_Cmd(DISABLE);
236 /* Configure structure initialization */
237 (void)RTC_StructInit(&stcRtcInit);
238
239 /* Configuration RTC structure */
240 stcRtcInit.u8ClockSrc = RTC_CLK_SRC_SEL;
241 stcRtcInit.u8HourFormat = RTC_HOUR_FMT_24H;
242 (void)RTC_Init(&stcRtcInit);
243
244 /* Clear all status */
245 RTC_ClearStatus(RTC_FLAG_CLR_ALL);
246 /* Startup RTC count */
247 RTC_Cmd(ENABLE);
248
249 #if defined(HC32F4A0) || defined(HC32F4A8)
250 /* Write sequence flag to backup register */
251 _bakup_reg_write();
252 #endif
253 LOG_D("rtc init success");
254 }
255 }
256 else
257 {
258 LOG_D("rtc does not need to init");
259 }
260
261 return RT_EOK;
262 }
263
_rtc_get_secs(time_t * sec)264 static rt_err_t _rtc_get_secs(time_t *sec)
265 {
266 struct timeval tv;
267
268 _rtc_get_timeval(&tv);
269 *(time_t *) sec = tv.tv_sec;
270 LOG_D("RTC: get rtc_time %d", *sec);
271
272 return RT_EOK;
273 }
274
_rtc_set_secs(time_t * sec)275 static rt_err_t _rtc_set_secs(time_t *sec)
276 {
277 rt_err_t result = RT_EOK;
278
279 if (hc32_rtc_set_time_stamp(*sec))
280 {
281 result = -RT_ERROR;
282 }
283 LOG_D("RTC: set rtc_time %d", *sec);
284 #ifdef RT_USING_ALARM
285 rt_alarm_update(&rtc_dev.parent, 1);
286 #endif
287 return result;
288 }
289
290 #ifdef RT_USING_ALARM
291
_rtc_alarm_irq_handler(void)292 static void _rtc_alarm_irq_handler(void)
293 {
294 rt_interrupt_enter();
295 RTC_ClearStatus(RTC_FLAG_ALARM);
296 rt_alarm_update(&rtc_dev.parent, 1);
297 rt_interrupt_leave();
298 }
299
300 #if defined(HC32F448) || defined(HC32F472)
RTC_Handler(void)301 void RTC_Handler(void)
302 {
303 if (RTC_GetStatus(RTC_FLAG_ALARM) != RESET)
304 {
305 _rtc_alarm_irq_handler();
306 }
307 }
308 #endif
309
hc32_rtc_alarm_enable(void)310 static void hc32_rtc_alarm_enable(void)
311 {
312 NVIC_EnableIRQ(hc32_alarm_irq.irq_config.irq_num);
313
314 RTC_IntCmd(RTC_INT_ALARM, ENABLE);
315 RTC_AlarmCmd(ENABLE);
316 LOG_D("hc32 alarm enable");
317 }
318
hc32_rtc_alarm_disable(void)319 static void hc32_rtc_alarm_disable(void)
320 {
321 RTC_AlarmCmd(DISABLE);
322 RTC_IntCmd(RTC_INT_ALARM, DISABLE);
323
324 NVIC_DisableIRQ(hc32_alarm_irq.irq_config.irq_num);
325 LOG_D("hc32 alarm disable");
326 }
327 #endif
328
_rtc_get_alarm(struct rt_rtc_wkalarm * alarm)329 static rt_err_t _rtc_get_alarm(struct rt_rtc_wkalarm *alarm)
330 {
331 #ifdef RT_USING_ALARM
332 stc_rtc_alarm_t stcRtcAlarm;
333 RTC_GetAlarm(RTC_DATA_FMT_DEC, &stcRtcAlarm);
334 alarm->tm_hour = stcRtcAlarm.u8AlarmHour;
335 alarm->tm_min = stcRtcAlarm.u8AlarmMinute;
336 alarm->tm_sec = 0; /* alarms precision is 1 minute */
337
338 LOG_D("GET_ALARM %d:%d:%d", alarm->tm_hour, alarm->tm_min, alarm->tm_sec);
339 return RT_EOK;
340 #else
341 return -RT_ERROR;
342 #endif
343 }
344
_rtc_set_alarm(struct rt_rtc_wkalarm * alarm)345 static rt_err_t _rtc_set_alarm(struct rt_rtc_wkalarm *alarm)
346 {
347 #ifdef RT_USING_ALARM
348 stc_rtc_alarm_t stcRtcAlarm;
349
350 LOG_D("RT_DEVICE_CTRL_RTC_SET_ALARM");
351 if (alarm != RT_NULL)
352 {
353 if (alarm->enable)
354 {
355 RTC_AlarmCmd(DISABLE);
356 /* Configuration alarm time: precision is 1 minute */
357 stcRtcAlarm.u8AlarmHour = alarm->tm_hour;
358 stcRtcAlarm.u8AlarmMinute = alarm->tm_min;
359 stcRtcAlarm.u8AlarmWeekday = RTC_ALARM_WEEKDAY_EVERYDAY;
360 stcRtcAlarm.u8AlarmAmPm = RTC_HOUR_24H;
361 RTC_ClearStatus(RTC_FLAG_ALARM);
362 (void)RTC_SetAlarm(RTC_DATA_FMT_DEC, &stcRtcAlarm);
363 hc32_rtc_alarm_enable();
364 LOG_D("SET_ALARM %d:%d:%d", alarm->tm_hour,
365 alarm->tm_min, 0);
366 }
367 else
368 {
369 hc32_rtc_alarm_disable();
370 }
371
372 }
373 else
374 {
375 LOG_E("RT_DEVICE_CTRL_RTC_SET_ALARM error!!");
376 return -RT_ERROR;
377 }
378
379 return RT_EOK;
380 #else
381 return -RT_ERROR;
382 #endif
383 }
384
385 const static struct rt_rtc_ops _ops =
386 {
387 _rtc_init,
388 _rtc_get_secs,
389 _rtc_set_secs,
390 _rtc_get_alarm,
391 _rtc_set_alarm,
392 _rtc_get_timeval,
393 RT_NULL
394 };
395
rt_hw_rtc_init(void)396 int rt_hw_rtc_init(void)
397 {
398 rt_err_t result;
399
400 #ifdef RT_USING_ALARM
401 /* register interrupt */
402 hc32_install_irq_handler(&hc32_alarm_irq.irq_config, hc32_alarm_irq.irq_callback, RT_FALSE);
403 #endif
404 rtc_dev.ops = &_ops;
405 result = rt_hw_rtc_register(&rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL);
406 if (result != RT_EOK)
407 {
408 LOG_E("rtc register err code: %d", result);
409 return result;
410 }
411 LOG_D("rtc register done");
412 return RT_EOK;
413 }
414 INIT_DEVICE_EXPORT(rt_hw_rtc_init);
415
416 #endif /* BSP_USING_RTC */
417