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 * 2025-08-19 Alex Yang Add MCXA346 RTC driver for RT-Thread
9 */
10
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 #include <sys/time.h>
14
15 #ifdef BSP_USING_RTC
16
17 #define DBG_TAG "drv.rtc"
18 #define DBG_LVL DBG_INFO
19 #include <rtdbg.h>
20
21 #include "fsl_rtc.h"
22 #include "fsl_clock.h"
23
24
25 /* Get RTC timestamp */
get_rtc_timestamp(void)26 static time_t get_rtc_timestamp(void)
27 {
28 rtc_datetime_t datetime;
29 struct tm tm_new;
30
31 /* Get current time from RTC */
32 RTC_GetDatetime(RTC0, &datetime);
33
34 tm_new.tm_sec = datetime.second;
35 tm_new.tm_min = datetime.minute;
36 tm_new.tm_hour = datetime.hour;
37 tm_new.tm_mday = datetime.day;
38 tm_new.tm_mon = datetime.month - 1;
39 tm_new.tm_year = datetime.year - 1900;
40 tm_new.tm_isdst = 0;
41
42 LOG_D("get rtc time: %04d-%02d-%02d %02d:%02d:%02d",
43 datetime.year, datetime.month, datetime.day,
44 datetime.hour, datetime.minute, datetime.second);
45
46 return mktime(&tm_new);
47 }
48
49 /* Set RTC timestamp */
set_rtc_time_stamp(time_t time_stamp)50 static rt_err_t set_rtc_time_stamp(time_t time_stamp)
51 {
52 rtc_datetime_t datetime;
53 struct tm *time_tm;
54
55 time_tm = gmtime(&time_stamp);
56 if (time_tm->tm_year < 70) /* Year should be >= 1970 */
57 {
58 LOG_E("Invalid year: %d", time_tm->tm_year + 1900);
59 return -RT_ERROR;
60 }
61
62 /* Convert to RTC datetime format */
63 datetime.year = time_tm->tm_year + 1900;
64 datetime.month = time_tm->tm_mon + 1;
65 datetime.day = time_tm->tm_mday;
66 datetime.hour = time_tm->tm_hour;
67 datetime.minute = time_tm->tm_min;
68 datetime.second = time_tm->tm_sec;
69
70 /* Set RTC time */
71 RTC_StopTimer(RTC0);
72
73 RTC_SetDatetime(RTC0, &datetime);
74
75 RTC_StartTimer(RTC0);
76
77 LOG_D("set rtc time: %04d-%02d-%02d %02d:%02d:%02d",
78 datetime.year, datetime.month, datetime.day,
79 datetime.hour, datetime.minute, datetime.second);
80
81 return RT_EOK;
82 }
83
84 /* RTC configuration */
rt_rtc_config(void)85 static rt_err_t rt_rtc_config(void)
86 {
87 rtc_config_t rtc_config;
88
89 /* Get default RTC configuration */
90 RTC_GetDefaultConfig(&rtc_config);
91
92 /* Initialize RTC - Note: RTC_Init returns void, not status */
93 RTC_Init(RTC0, &rtc_config);
94
95 /* Start RTC timer */
96 RTC_StartTimer(RTC0);
97
98 return RT_EOK;
99 }
100
101 /* RTC initialization */
_rtc_init(void)102 static rt_err_t _rtc_init(void)
103 {
104
105 /* Configure RTC */
106 if (rt_rtc_config() != RT_EOK)
107 {
108 LOG_E("RTC config failed.");
109 return -RT_ERROR;
110 }
111
112 LOG_D("RTC initialized successfully");
113 return RT_EOK;
114 }
115
116 /* Get RTC seconds */
_rtc_get_secs(time_t * args)117 static rt_err_t _rtc_get_secs(time_t *args)
118 {
119 RT_ASSERT(args != RT_NULL);
120
121 *args = get_rtc_timestamp();
122 LOG_D("RTC: get rtc_time %x", *args);
123
124 return RT_EOK;
125 }
126
127 /* Set RTC seconds */
_rtc_set_secs(time_t * args)128 static rt_err_t _rtc_set_secs(time_t *args)
129 {
130 rt_err_t result = RT_EOK;
131
132 RT_ASSERT(args != RT_NULL);
133
134 if (set_rtc_time_stamp(*args) != RT_EOK)
135 {
136 result = -RT_ERROR;
137 }
138 LOG_D("RTC: set rtc_time %x", *args);
139
140 return result;
141 }
142
143 /* Get RTC alarm */
_rtc_get_alarm(struct rt_rtc_wkalarm * wkalarm)144 static rt_err_t _rtc_get_alarm(struct rt_rtc_wkalarm *wkalarm)
145 {
146 rtc_datetime_t datetime;
147
148 RT_ASSERT(wkalarm != RT_NULL);
149
150 /* Get alarm time from RTC */
151 RTC_GetAlarm(RTC0, &datetime);
152
153 /* Convert to wkalarm format */
154 wkalarm->tm_sec = datetime.second;
155 wkalarm->tm_min = datetime.minute;
156 wkalarm->tm_hour = datetime.hour;
157 wkalarm->tm_mday = datetime.day;
158 wkalarm->tm_mon = datetime.month - 1;
159 wkalarm->tm_year = datetime.year - 1900;
160
161 /* Check if alarm is enabled */
162 wkalarm->enable = (RTC_GetEnabledInterrupts(RTC0) & kRTC_AlarmInterruptEnable) ? 1 : 0;
163
164 LOG_D("RTC: get alarm %04d-%02d-%02d %02d:%02d:%02d (%s)",
165 datetime.year, datetime.month, datetime.day,
166 datetime.hour, datetime.minute, datetime.second,
167 wkalarm->enable ? "ENABLED" : "DISABLED");
168
169 return RT_EOK;
170 }
171
172 /* Set RTC alarm */
_rtc_set_alarm(struct rt_rtc_wkalarm * wkalarm)173 static rt_err_t _rtc_set_alarm(struct rt_rtc_wkalarm *wkalarm)
174 {
175 rtc_datetime_t datetime;
176
177 RT_ASSERT(wkalarm != RT_NULL);
178
179 /* Convert from wkalarm format */
180 datetime.year = wkalarm->tm_year + 1900;
181 datetime.month = wkalarm->tm_mon + 1;
182 datetime.day = wkalarm->tm_mday;
183 datetime.hour = wkalarm->tm_hour;
184 datetime.minute = wkalarm->tm_min;
185 datetime.second = wkalarm->tm_sec;
186
187 /* Set alarm time */
188 RTC_SetAlarm(RTC0, &datetime);
189
190 /* Enable/disable alarm interrupt */
191 if (wkalarm->enable)
192 {
193 RTC_EnableInterrupts(RTC0, kRTC_AlarmInterruptEnable);
194 EnableIRQ(RTC_IRQn); /* Use RTC_IRQn instead of RTC0_IRQn */
195 LOG_D("RTC alarm enabled");
196 }
197 else
198 {
199 RTC_DisableInterrupts(RTC0, kRTC_AlarmInterruptEnable);
200 LOG_D("RTC alarm disabled");
201 }
202
203 LOG_D("RTC: set alarm %04d-%02d-%02d %02d:%02d:%02d",
204 datetime.year, datetime.month, datetime.day,
205 datetime.hour, datetime.minute, datetime.second);
206
207 return RT_EOK;
208 }
209
210 /* RTC operations structure */
211 static const struct rt_rtc_ops _rtc_ops =
212 {
213 _rtc_init,
214 _rtc_get_secs,
215 _rtc_set_secs,
216 _rtc_get_alarm,
217 _rtc_set_alarm,
218 RT_NULL, /* get_timeval */
219 RT_NULL, /* set_timeval */
220 };
221
222 static rt_rtc_dev_t mcxa_rtc_dev;
223
224 /* RTC interrupt handler */
RTC_IRQHandler(void)225 void RTC_IRQHandler(void)
226 {
227 rt_interrupt_enter();
228
229 /* Get interrupt status */
230 uint32_t status = RTC_GetStatusFlags(RTC0);
231
232 /* Handle alarm interrupt */
233 if (status & kRTC_AlarmFlag)
234 {
235 /* Clear alarm flag */
236 RTC_ClearStatusFlags(RTC0, kRTC_AlarmFlag);
237
238 LOG_D("RTC alarm triggered");
239
240 /* If alarm framework is available, notify it */
241 #ifdef RT_USING_ALARM
242 /* Send alarm event to alarm thread */
243 rt_event_send(&_container.event, 1);
244 #endif
245 }
246
247 /* Handle seconds interrupt if needed */
248 if (status & kRTC_SecondsInterruptEnable)
249 {
250 LOG_D("RTC seconds interrupt");
251 }
252
253 rt_interrupt_leave();
254 }
255
256 /* Hardware RTC initialization */
rt_hw_rtc_init(void)257 int rt_hw_rtc_init(void)
258 {
259 rt_err_t result;
260
261 /* Set RTC operations */
262 mcxa_rtc_dev.ops = &_rtc_ops;
263
264 /* Register RTC device */
265 result = rt_hw_rtc_register(&mcxa_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL);
266 if (result != RT_EOK)
267 {
268 LOG_E("RTC register failed, err code: %d", result);
269 return result;
270 }
271
272 LOG_D("RTC init success");
273 return RT_EOK;
274 }
275
276 INIT_DEVICE_EXPORT(rt_hw_rtc_init);
277
278 #endif /* BSP_USING_RTC */
279