1 /*
2 * Copyright (c) 2006-2025, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2022-01-25 iysheng first version
9 */
10
11 #include <board.h>
12 #include <sys/time.h>
13
14 #define DBG_TAG "drv.rtc"
15 #define DBG_LVL DBG_INFO
16
17 #include <rtdbg.h>
18
19 #ifdef RT_USING_RTC
20
21 typedef struct {
22 struct rt_device rtc_dev;
23 } gd32_rtc_device;
24
25 static gd32_rtc_device g_gd32_rtc_dev;
26
27 /**
28 * @brief Helper function: Convert BCD value to binary.
29 * @param val: BCD value.
30 * @return Binary value.
31 */
bcd_to_bin(rt_uint8_t val)32 static rt_uint8_t bcd_to_bin(rt_uint8_t val)
33 {
34 return (val & 0x0F) + ((val >> 4) & 0x0F) * 10;
35 }
36
37 /**
38 * @brief Helper function: Convert binary to BCD.
39 * @param val: Binary value.
40 * @return BCD value.
41 */
bin_to_bcd(rt_uint8_t val)42 static rt_uint8_t bin_to_bcd(rt_uint8_t val)
43 {
44 return ((val / 10) << 4) | (val % 10);
45 }
46
get_rtc_timestamp(void)47 static time_t get_rtc_timestamp(void)
48 {
49 #if defined SOC_SERIES_GD32E23x
50 struct tm tm_new;
51 rtc_parameter_struct rtc_current_time;
52
53 rtc_current_time_get(&rtc_current_time);
54
55 tm_new.tm_year = bcd_to_bin(rtc_current_time.rtc_year) + 100; /* tm_year: years since 1900 */
56 tm_new.tm_mon = bcd_to_bin(rtc_current_time.rtc_month) - 1; /* tm_mon: month (0 = January, 11 = December) */
57 tm_new.tm_mday = bcd_to_bin(rtc_current_time.rtc_date);
58 tm_new.tm_hour = bcd_to_bin(rtc_current_time.rtc_hour);
59 tm_new.tm_min = bcd_to_bin(rtc_current_time.rtc_minute);
60 tm_new.tm_sec = bcd_to_bin(rtc_current_time.rtc_second);
61
62 return mktime(&tm_new);
63 #else
64 time_t rtc_counter;
65
66 rtc_counter = (time_t)rtc_counter_get();
67
68 return rtc_counter;
69 #endif
70 }
71
set_rtc_timestamp(time_t time_stamp)72 static rt_err_t set_rtc_timestamp(time_t time_stamp)
73 {
74 #if defined SOC_SERIES_GD32E23x
75 struct tm *p_tm;
76 rtc_parameter_struct rtc_init_struct;
77
78 p_tm = gmtime(&time_stamp);
79
80 /* GD32 RTC uses year starting from 2000; thus tm_year must be at least 100 (i.e., 2000 - 1900) */
81
82 if (p_tm->tm_year < 100)
83 {
84 return -RT_ERROR;
85 }
86
87 rtc_init_struct.rtc_year = bin_to_bcd(p_tm->tm_year - 100);
88 rtc_init_struct.rtc_month = bin_to_bcd(p_tm->tm_mon + 1);
89 rtc_init_struct.rtc_date = bin_to_bcd(p_tm->tm_mday);
90
91 rtc_init_struct.rtc_day_of_week = bin_to_bcd(p_tm->tm_wday == 0 ? 7 : p_tm->tm_wday);
92 rtc_init_struct.rtc_hour = bin_to_bcd(p_tm->tm_hour);
93 rtc_init_struct.rtc_minute = bin_to_bcd(p_tm->tm_min);
94 rtc_init_struct.rtc_second = bin_to_bcd(p_tm->tm_sec);
95 rtc_init_struct.rtc_display_format = RTC_24HOUR;
96
97 #if defined(BSP_RTC_USING_LSI)
98 rtc_init_struct.rtc_factor_asyn = 39;
99 rtc_init_struct.rtc_factor_syn = 999;
100 #elif defined(BSP_RTC_USING_LSE)
101 rtc_init_struct.rtc_factor_asyn = 127;
102 rtc_init_struct.rtc_factor_syn = 255;
103 #endif
104
105 if (rtc_init(&rtc_init_struct) != SUCCESS)
106 {
107 LOG_E("Failed to set RTC time.");
108 return -RT_ERROR;
109 }
110
111 return RT_EOK;
112 #else
113 uint32_t rtc_counter;
114
115 rtc_counter = (uint32_t)time_stamp;
116
117 /* wait until LWOFF bit in RTC_CTL to 1 */
118 rtc_lwoff_wait();
119 /* enter configure mode */
120 rtc_configuration_mode_enter();
121 /* write data to rtc register */
122 rtc_counter_set(rtc_counter);
123 /* exit configure mode */
124 rtc_configuration_mode_exit();
125 /* wait until LWOFF bit in RTC_CTL to 1 */
126 rtc_lwoff_wait();
127
128 return RT_EOK;
129 #endif
130 }
131
rt_gd32_rtc_control(rt_device_t dev,int cmd,void * args)132 static rt_err_t rt_gd32_rtc_control(rt_device_t dev, int cmd, void *args)
133 {
134 rt_err_t result = RT_EOK;
135
136 RT_ASSERT(dev != RT_NULL);
137 switch (cmd)
138 {
139 case RT_DEVICE_CTRL_RTC_GET_TIME:
140 *(rt_uint32_t *)args = get_rtc_timestamp();
141 break;
142
143 case RT_DEVICE_CTRL_RTC_SET_TIME:
144 if (set_rtc_timestamp(*(rt_uint32_t *)args))
145 {
146 result = -RT_ERROR;
147 }
148 break;
149 }
150
151 return result;
152 }
153
154 #ifdef RT_USING_DEVICE_OPS
155 const static struct rt_device_ops g_gd32_rtc_ops =
156 {
157 RT_NULL,
158 RT_NULL,
159 RT_NULL,
160 RT_NULL,
161 RT_NULL,
162 rt_gd32_rtc_control
163 };
164 #endif
165
rt_hw_rtc_init(void)166 static int rt_hw_rtc_init(void)
167 {
168 rt_err_t ret;
169 time_t rtc_counter;
170
171 rcu_periph_clock_enable(RCU_PMU);
172 pmu_backup_write_enable();
173 #ifndef SOC_SERIES_GD32E23x
174 rcu_periph_clock_enable(RCU_BKPI);
175 #endif
176
177 rtc_counter = get_rtc_timestamp();
178 /* once the rtc clock source has been selected, if can't be changed
179 * anymore unless the Backup domain is reset */
180 rcu_bkp_reset_enable();
181 rcu_bkp_reset_disable();
182 rcu_periph_clock_enable(RCU_RTC);
183 #if defined(BSP_RTC_USING_LSE)
184 rcu_osci_on(RCU_LXTAL);
185 if (SUCCESS == rcu_osci_stab_wait(RCU_LXTAL))
186 {
187 /* set lxtal as rtc clock source */
188 rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
189 }
190 #elif defined(BSP_RTC_USING_LSI)
191 rcu_osci_on(RCU_IRC40K);
192 if (SUCCESS == rcu_osci_stab_wait(RCU_IRC40K))
193 {
194 /* set IRC40K as rtc clock source */
195 rcu_rtc_clock_config(RCU_RTCSRC_IRC40K);
196 }
197 #endif
198 set_rtc_timestamp(rtc_counter);
199
200 #ifdef RT_USING_DEVICE_OPS
201 g_gd32_rtc_dev.rtc_dev.ops = &g_gd32_rtc_ops;
202 #else
203 g_gd32_rtc_dev.rtc_dev.init = RT_NULL;
204 g_gd32_rtc_dev.rtc_dev.open = RT_NULL;
205 g_gd32_rtc_dev.rtc_dev.close = RT_NULL;
206 g_gd32_rtc_dev.rtc_dev.read = RT_NULL;
207 g_gd32_rtc_dev.rtc_dev.write = RT_NULL;
208 g_gd32_rtc_dev.rtc_dev.control = rt_gd32_rtc_control;
209 #endif
210 g_gd32_rtc_dev.rtc_dev.type = RT_Device_Class_RTC;
211 g_gd32_rtc_dev.rtc_dev.rx_indicate = RT_NULL;
212 g_gd32_rtc_dev.rtc_dev.tx_complete = RT_NULL;
213 g_gd32_rtc_dev.rtc_dev.user_data = RT_NULL;
214
215 ret = rt_device_register(&g_gd32_rtc_dev.rtc_dev, "rtc", \
216 RT_DEVICE_FLAG_RDWR);
217 if (ret != RT_EOK)
218 {
219 LOG_E("failed register internal rtc device, err=%d", ret);
220 }
221
222 return ret;
223 }
224 INIT_DEVICE_EXPORT(rt_hw_rtc_init);
225 #endif
226
227