1 /*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * Copyright (c) 2024 Andriy Gelman
5 * Author: Andriy Gelman <andriy.gelman@gmail.com>
6 */
7
8 #include <zephyr/drivers/rtc.h>
9 #include <zephyr/logging/log.h>
10 #include <zephyr/sys/util.h>
11 #include <zephyr/kernel.h>
12
13 #include <soc.h>
14 #include <xmc_rtc.h>
15 #include <xmc_scu.h>
16
17 #define DT_DRV_COMPAT infineon_xmc4xxx_rtc
18
19 #define RTC_XMC4XXX_DEFAULT_PRESCALER 0x7fff
20
21 #define RTC_XMC4XXX_SUPPORTED_ALARM_MASK \
22 (RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR | \
23 RTC_ALARM_TIME_MASK_MONTHDAY | RTC_ALARM_TIME_MASK_MONTH | RTC_ALARM_TIME_MASK_YEAR)
24
25 struct rtc_xmc4xxx_data {
26 #if defined(CONFIG_RTC_ALARM)
27 rtc_alarm_callback alarm_callback;
28 void *alarm_user_data;
29 #endif
30 #if defined(CONFIG_RTC_UPDATE)
31 rtc_update_callback update_callback;
32 void *update_user_data;
33 #endif
34 };
35
rtc_xmc4xxx_set_time(const struct device * dev,const struct rtc_time * timeptr)36 static int rtc_xmc4xxx_set_time(const struct device *dev, const struct rtc_time *timeptr)
37 {
38 const struct tm *stdtime;
39
40 if (timeptr == NULL) {
41 return -EINVAL;
42 }
43
44 XMC_RTC_Stop();
45
46 stdtime = rtc_time_to_tm((struct rtc_time *)timeptr);
47 XMC_RTC_SetTimeStdFormat(stdtime);
48
49 XMC_RTC_Start();
50
51 return 0;
52 }
53
rtc_xmc4xxx_get_time(const struct device * dev,struct rtc_time * timeptr)54 static int rtc_xmc4xxx_get_time(const struct device *dev, struct rtc_time *timeptr)
55 {
56 struct tm *stdtime = rtc_time_to_tm(timeptr);
57
58 if (!XMC_RTC_IsRunning()) {
59 return -ENODATA;
60 }
61
62 if (stdtime == NULL) {
63 return -EINVAL;
64 }
65
66 XMC_RTC_GetTimeStdFormat(stdtime);
67 timeptr->tm_nsec = 0;
68
69 return 0;
70 }
71
72 #if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE)
rtc_xmc4xxx_isr(const struct device * dev)73 static void rtc_xmc4xxx_isr(const struct device *dev)
74 {
75 struct rtc_xmc4xxx_data *dev_data = dev->data;
76
77 uint32_t event = SCU_INTERRUPT->SRRAW;
78
79 #if defined(CONFIG_RTC_ALARM)
80 if ((event & XMC_SCU_INTERRUPT_EVENT_RTC_ALARM) != 0) {
81 if (dev_data->alarm_callback != NULL) {
82 dev_data->alarm_callback(dev, 0, dev_data->alarm_user_data);
83 }
84 XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM);
85 }
86 #endif
87
88 #if defined(CONFIG_RTC_UPDATE)
89 if ((event & XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC) != 0) {
90 if (dev_data->update_callback != NULL) {
91 dev_data->update_callback(dev, dev_data->update_user_data);
92 }
93 XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC);
94 }
95 #endif
96 }
97 #endif
98
99 #ifdef CONFIG_RTC_ALARM
rtc_xmc4xxx_alarm_get_supported_fields(const struct device * dev,uint16_t id,uint16_t * mask)100 static int rtc_xmc4xxx_alarm_get_supported_fields(const struct device *dev, uint16_t id,
101 uint16_t *mask)
102 {
103 ARG_UNUSED(dev);
104 ARG_UNUSED(id);
105
106 *mask = RTC_XMC4XXX_SUPPORTED_ALARM_MASK;
107 return 0;
108 }
109
rtc_xmc4xxx_alarm_set_time(const struct device * dev,uint16_t id,uint16_t mask,const struct rtc_time * timeptr)110 static int rtc_xmc4xxx_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask,
111 const struct rtc_time *timeptr)
112 {
113 const struct tm *stdtime = rtc_time_to_tm((struct rtc_time *)timeptr);
114
115 if (id != 0 || (mask > 0 && timeptr == NULL)) {
116 return -EINVAL;
117 }
118
119 if (mask == 0) {
120 XMC_RTC_DisableEvent(XMC_RTC_EVENT_ALARM);
121 XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM);
122 return 0;
123 }
124
125 if (mask != RTC_XMC4XXX_SUPPORTED_ALARM_MASK) {
126 return -EINVAL;
127 }
128
129 XMC_RTC_SetAlarmStdFormat(stdtime);
130 XMC_RTC_EnableEvent(XMC_RTC_EVENT_ALARM);
131
132 return 0;
133 }
134
rtc_xmc4xxx_alarm_get_time(const struct device * dev,uint16_t id,uint16_t * mask,struct rtc_time * timeptr)135 static int rtc_xmc4xxx_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask,
136 struct rtc_time *timeptr)
137 {
138 ARG_UNUSED(dev);
139 struct tm *stdtime = rtc_time_to_tm(timeptr);
140
141 if (id != 0 || mask == NULL || timeptr == NULL) {
142 return -EINVAL;
143 }
144
145 *mask = RTC_XMC4XXX_SUPPORTED_ALARM_MASK;
146
147 XMC_RTC_GetAlarmStdFormat(stdtime);
148
149 return 0;
150 }
151
rtc_xmc4xxx_alarm_is_pending(const struct device * dev,uint16_t id)152 static int rtc_xmc4xxx_alarm_is_pending(const struct device *dev, uint16_t id)
153 {
154 ARG_UNUSED(dev);
155 unsigned int key;
156 int alarm = 0;
157 uint32_t event;
158
159 if (id != 0) {
160 return -EINVAL;
161 }
162
163 key = irq_lock();
164 event = SCU_INTERRUPT->SRRAW;
165
166 if ((event & XMC_SCU_INTERRUPT_EVENT_RTC_ALARM) != 0) {
167 alarm = 1;
168 XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM);
169 }
170 irq_unlock(key);
171
172 return alarm;
173 }
174
rtc_xmc4xxx_alarm_set_callback(const struct device * dev,uint16_t id,rtc_alarm_callback callback,void * user_data)175 static int rtc_xmc4xxx_alarm_set_callback(const struct device *dev, uint16_t id,
176 rtc_alarm_callback callback, void *user_data)
177 {
178 struct rtc_xmc4xxx_data *dev_data = dev->data;
179 unsigned int key;
180
181 if (id != 0) {
182 return -EINVAL;
183 }
184
185 key = irq_lock();
186 dev_data->alarm_callback = callback;
187 dev_data->alarm_user_data = user_data;
188 irq_unlock(key);
189
190 if (dev_data->alarm_callback) {
191 XMC_SCU_INTERRUPT_EnableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM);
192 } else {
193 XMC_SCU_INTERRUPT_DisableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM);
194 }
195
196 return 0;
197 }
198 #endif /* CONFIG_RTC_ALARM */
199
200 #ifdef CONFIG_RTC_UPDATE
rtc_xmc4xxx_update_set_callback(const struct device * dev,rtc_update_callback callback,void * user_data)201 static int rtc_xmc4xxx_update_set_callback(const struct device *dev, rtc_update_callback callback,
202 void *user_data)
203 {
204 struct rtc_xmc4xxx_data *dev_data = dev->data;
205 unsigned int key;
206
207 key = irq_lock();
208 dev_data->update_callback = callback;
209 dev_data->update_user_data = user_data;
210 irq_unlock(key);
211
212 if (dev_data->update_callback) {
213 XMC_RTC_EnableEvent(XMC_RTC_EVENT_PERIODIC_SECONDS);
214 XMC_SCU_INTERRUPT_EnableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC);
215 } else {
216 XMC_SCU_INTERRUPT_DisableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC);
217 XMC_RTC_DisableEvent(XMC_RTC_EVENT_PERIODIC_SECONDS);
218 }
219
220 return 0;
221 }
222 #endif /* CONFIG_RTC_UPDATE */
223
224 static DEVICE_API(rtc, rtc_xmc4xxx_driver_api) = {
225 .set_time = rtc_xmc4xxx_set_time,
226 .get_time = rtc_xmc4xxx_get_time,
227 #ifdef CONFIG_RTC_ALARM
228 .alarm_get_supported_fields = rtc_xmc4xxx_alarm_get_supported_fields,
229 .alarm_set_time = rtc_xmc4xxx_alarm_set_time,
230 .alarm_get_time = rtc_xmc4xxx_alarm_get_time,
231 .alarm_is_pending = rtc_xmc4xxx_alarm_is_pending,
232 .alarm_set_callback = rtc_xmc4xxx_alarm_set_callback,
233 #endif
234 #ifdef CONFIG_RTC_UPDATE
235 .update_set_callback = rtc_xmc4xxx_update_set_callback,
236 #endif
237 };
238
239 #if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE)
rtc_xmc4xxx_irq_config(void)240 static void rtc_xmc4xxx_irq_config(void)
241 {
242 /* RTC and watchdog share the same interrupt. Shared interrupts must */
243 /* be enabled if WDT is enabled and RTC is using alarm or update feature */
244 IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), rtc_xmc4xxx_isr,
245 DEVICE_DT_INST_GET(0), 0);
246 irq_enable(DT_INST_IRQN(0));
247 }
248 #endif
249
rtc_xmc4xxx_init(const struct device * dev)250 static int rtc_xmc4xxx_init(const struct device *dev)
251 {
252 if (!XMC_RTC_IsRunning()) {
253 if (!XMC_SCU_HIB_IsHibernateDomainEnabled()) {
254 XMC_SCU_HIB_EnableHibernateDomain();
255 }
256 XMC_RTC_SetPrescaler(RTC_XMC4XXX_DEFAULT_PRESCALER);
257 }
258
259 #if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE)
260 rtc_xmc4xxx_irq_config();
261 #endif
262
263 return 0;
264 }
265
266
267 static struct rtc_xmc4xxx_data rtc_xmc4xxx_data_0;
268
269 DEVICE_DT_INST_DEFINE(0, rtc_xmc4xxx_init, NULL, &rtc_xmc4xxx_data_0, NULL,
270 POST_KERNEL, CONFIG_RTC_INIT_PRIORITY, &rtc_xmc4xxx_driver_api);
271