1 /*
2  * Copyright (c) 2006-2024 RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date         Author         Notes
8  * 2022-07-25   Rbb666         first version
9  * 2024-11-06   kurisaw        add alarm function
10  */
11 
12 #include <rtthread.h>
13 #include <rtdevice.h>
14 #include <sys/time.h>
15 #include "drv_common.h"
16 
17 #ifdef BSP_USING_RTC
18 
19 /*#define DRV_DEBUG*/
20 #define LOG_TAG                         "drv.rtc"
21 #include <drv_log.h>
22 
23 cyhal_rtc_t rtc_obj;
24 
25 struct rtc_device_object
26 {
27     rt_rtc_dev_t  rtc_dev;
28 #ifdef RT_USING_ALARM
29     struct rt_rtc_wkalarm   wkalarm;
30 #endif
31 };
32 
33 static struct rtc_device_object ifx32_rtc_dev;
34 
get_day_of_week(int day,int month,int year)35 static int get_day_of_week(int day, int month, int year)
36 {
37     int ret;
38     int k = 0;
39     int j = 0;
40 
41     if (month < CY_RTC_MARCH)
42     {
43         month += CY_RTC_MONTHS_PER_YEAR;
44         year--;
45     }
46 
47     k = (year % 100);
48     j = (year / 100);
49     ret = (day + (13 * (month + 1) / 5) + k + (k / 4) + (j / 4) + (5 * j)) % 7;
50     ret = ((ret + 6) % 7);
51 
52     return ret;
53 }
54 
set_rtc_time_stamp(time_t time_stamp)55 static rt_err_t set_rtc_time_stamp(time_t time_stamp)
56 {
57     struct tm tm = {0};
58     struct tm new_time = {0};
59 
60     gmtime_r(&time_stamp, &tm);
61 
62     if (tm.tm_year < 100)
63     {
64         return -RT_ERROR;
65     }
66 
67     new_time.tm_sec     = tm.tm_sec ;
68     new_time.tm_min     = tm.tm_min ;
69     new_time.tm_hour    = tm.tm_hour;
70     new_time.tm_mday    = tm.tm_mday;
71     new_time.tm_mon     = tm.tm_mon;
72     new_time.tm_year    = tm.tm_year;
73     new_time.tm_wday    = get_day_of_week(tm.tm_mday, tm.tm_mon, tm.tm_year);
74 
75     if (cyhal_rtc_write(&rtc_obj, &new_time) != RT_EOK)
76     {
77         return -RT_ERROR;
78     }
79 
80     LOG_D("set rtc time.");
81 
82     return RT_EOK;
83 }
84 
ifx_rtc_get_timeval(struct timeval * tv)85 static rt_err_t ifx_rtc_get_timeval(struct timeval *tv)
86 {
87     struct tm tm_new = {0};
88     struct tm date_time = {0};
89 
90     cyhal_rtc_read(&rtc_obj, &date_time);
91 
92     tm_new.tm_sec  = date_time.tm_sec;
93     tm_new.tm_min  = date_time.tm_min;
94     tm_new.tm_hour = date_time.tm_hour;
95     tm_new.tm_mday = date_time.tm_mday;
96     tm_new.tm_mon  = date_time.tm_mon;
97     tm_new.tm_year = date_time.tm_year;
98 
99     tv->tv_sec = timegm(&tm_new);
100 
101     return RT_EOK;
102 }
103 
_rtc_init(void)104 static rt_err_t _rtc_init(void)
105 {
106 #ifdef BSP_RTC_USING_LSE
107     Cy_RTC_SelectClockSource(CY_RTC_CLK_SELECT_WCO);
108 #else
109     Cy_RTC_SelectClockSource(CY_RTC_CLK_SELECT_ILO);
110 #endif /* BSP_RTC_USING_LSE */
111     if (cyhal_rtc_init(&rtc_obj) != RT_EOK)
112     {
113         LOG_E("rtc init failed.");
114         return -RT_ERROR;
115     }
116 
117 #ifdef RT_USING_ALARM
118     cyhal_rtc_register_callback(&rtc_obj, rtc_alarm_callback, NULL);
119     cyhal_rtc_enable_event(&rtc_obj, CYHAL_RTC_ALARM, 3u, true);
120 #endif
121     return RT_EOK;
122 }
123 
_rtc_get_secs(time_t * sec)124 static rt_err_t _rtc_get_secs(time_t *sec)
125 {
126     struct timeval tv;
127 
128     ifx_rtc_get_timeval(&tv);
129     *(time_t *) sec = tv.tv_sec;
130     LOG_D("RTC: get rtc_time %d", *sec);
131 
132     return RT_EOK;
133 }
134 
_rtc_set_secs(time_t * sec)135 static rt_err_t _rtc_set_secs(time_t *sec)
136 {
137     rt_err_t result = RT_EOK;
138 
139     if (set_rtc_time_stamp(*sec))
140     {
141         result = -RT_ERROR;
142     }
143 
144     LOG_D("RTC: set rtc_time %d", *sec);
145 
146     return result;
147 }
148 
149 #if defined(RT_USING_ALARM)
150 
_rtc_get_alarm(struct rt_rtc_wkalarm * alarm)151 static rt_err_t _rtc_get_alarm(struct rt_rtc_wkalarm *alarm)
152 {
153 #ifdef RT_USING_ALARM
154     *alarm = ifx32_rtc_dev.wkalarm;
155     LOG_D("GET_ALARM %d:%d:%d",ifx32_rtc_dev.wkalarm.tm_hour,
156         ifx32_rtc_dev.wkalarm.tm_min,ifx32_rtc_dev.wkalarm.tm_sec);
157     return RT_EOK;
158 #else
159     return -RT_ERROR;
160 #endif
161 }
162 
_rtc_set_alarm(struct rt_rtc_wkalarm * alarm)163 static rt_err_t _rtc_set_alarm(struct rt_rtc_wkalarm *alarm)
164 {
165 #ifdef RT_USING_ALARM
166     LOG_D("RT_DEVICE_CTRL_RTC_SET_ALARM");
167     if (alarm != RT_NULL)
168     {
169         ifx32_rtc_dev.wkalarm.enable = alarm->enable;
170         ifx32_rtc_dev.wkalarm.tm_hour = alarm->tm_hour;
171         ifx32_rtc_dev.wkalarm.tm_min = alarm->tm_min;
172         ifx32_rtc_dev.wkalarm.tm_sec = alarm->tm_sec;
173 
174         cyhal_rtc_set_alarm_by_seconds(&rtc_obj, 1);
175     }
176     else
177     {
178         LOG_E("RT_DEVICE_CTRL_RTC_SET_ALARM error!!");
179         return -RT_ERROR;
180     }
181     LOG_D("SET_ALARM %d:%d:%d",alarm->tm_hour,
182         alarm->tm_min, alarm->tm_sec);
183     return RT_EOK;
184 #else
185     return -RT_ERROR;
186 #endif
187 }
188 
189 #ifdef RT_USING_ALARM
rtc_alarm_callback(void)190 void rtc_alarm_callback(void)
191 {
192     rt_interrupt_enter();
193     rt_alarm_update(0, 0);
194     rt_interrupt_leave();
195 }
196 #endif
197 
198 static const struct rt_rtc_ops _rtc_ops =
199 {
200     _rtc_init,
201     _rtc_get_secs,
202     _rtc_set_secs,
203     _rtc_get_alarm,
204     _rtc_set_alarm,
205     ifx_rtc_get_timeval,
206     RT_NULL,
207 };
208 
209 /**
210  * @brief    RTC initialization function.
211  *
212  * @return   RT_EOK indicates successful initialization, other value indicates failed;
213  */
rt_hw_rtc_init(void)214 static int rt_hw_rtc_init(void)
215 {
216     rt_err_t result = RT_EOK;
217 
218     ifx32_rtc_dev.rtc_dev.ops = &_rtc_ops;
219 
220     if (rt_hw_rtc_register(&(ifx32_rtc_dev.rtc_dev), "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL) != RT_EOK)
221     {
222         LOG_E("rtc init failed");
223         result = -RT_ERROR;
224     }
225     else
226     {
227         LOG_D("rtc init success");
228     }
229 
230     return result;
231 }
232 
233 INIT_DEVICE_EXPORT(rt_hw_rtc_init);
234 #endif
235