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