1 /*
2 * Copyright (c) 2025 Linumiz GmbH
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 */
7
8 #define DT_DRV_COMPAT ti_mspm0_rtc
9
10 #include <zephyr/device.h>
11 #include <zephyr/devicetree.h>
12 #include <zephyr/drivers/rtc.h>
13 #include <zephyr/init.h>
14 #include <zephyr/irq.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/sys/util_macro.h>
17 #include <zephyr/sys/__assert.h>
18 #include "rtc_utils.h"
19 #include <ti/driverlib/dl_rtc_common.h>
20
21 #if defined(CONFIG_RTC_ALARM)
22 #define RTC_TI_ALARM_1 0
23 #define RTC_TI_ALARM_2 1
24 #define RTC_TI_MAX_ALARM DT_INST_PROP(0, alarms_count)
25
26 BUILD_ASSERT((RTC_TI_MAX_ALARM != 0),
27 "CONFIG_RTC_ALARM is enabled, without setting alarms-count property");
28 #endif
29
30 struct rtc_ti_mspm0_config {
31 RTC_Regs *regs;
32 #if defined(CONFIG_RTC_ALARM)
33 void (*irq_config_func)(void);
34 #endif
35 bool rtc_x;
36 };
37
38 #if defined(CONFIG_RTC_ALARM)
39 struct rtc_ti_mspm0_alarm {
40 rtc_alarm_callback callback;
41 void *user_data;
42 uint16_t mask;
43 bool is_pending;
44 };
45 #endif
46
47 struct rtc_ti_mspm0_data {
48 struct k_spinlock lock;
49 #if defined(CONFIG_RTC_ALARM)
50 struct rtc_ti_mspm0_alarm rtc_alarm[RTC_TI_MAX_ALARM];
51 #endif
52 };
53
rtc_ti_mspm0_set_time(const struct device * dev,const struct rtc_time * timeptr)54 static int rtc_ti_mspm0_set_time(const struct device *dev,
55 const struct rtc_time *timeptr)
56 {
57 const struct rtc_ti_mspm0_config *cfg = dev->config;
58 struct rtc_ti_mspm0_data *data = dev->data;
59
60 if ((timeptr == NULL) || !rtc_utils_validate_rtc_time(timeptr, 0)) {
61 return -EINVAL;
62 }
63
64 K_SPINLOCK(&data->lock) {
65 DL_RTC_Common_setCalendarSecondsBinary(cfg->regs, timeptr->tm_sec);
66 DL_RTC_Common_setCalendarMinutesBinary(cfg->regs, timeptr->tm_min);
67 DL_RTC_Common_setCalendarHoursBinary(cfg->regs, timeptr->tm_hour);
68 DL_RTC_Common_setCalendarDayOfWeekBinary(cfg->regs, timeptr->tm_wday);
69 DL_RTC_Common_setCalendarDayOfMonthBinary(cfg->regs, timeptr->tm_mday);
70 DL_RTC_Common_setCalendarMonthBinary(cfg->regs, timeptr->tm_mon);
71 DL_RTC_Common_setCalendarYearBinary(cfg->regs, timeptr->tm_year);
72 }
73
74 return 0;
75 }
76
rtc_ti_mspm0_get_time(const struct device * dev,struct rtc_time * timeptr)77 static int rtc_ti_mspm0_get_time(const struct device *dev,
78 struct rtc_time *timeptr)
79 {
80 const struct rtc_ti_mspm0_config *cfg = dev->config;
81 struct rtc_ti_mspm0_data *data = dev->data;
82
83 if (timeptr == NULL) {
84 return -EINVAL;
85 }
86
87 K_SPINLOCK(&data->lock) {
88 timeptr->tm_sec = DL_RTC_Common_getCalendarSecondsBinary(cfg->regs);
89 timeptr->tm_min = DL_RTC_Common_getCalendarMinutesBinary(cfg->regs);
90 timeptr->tm_hour = DL_RTC_Common_getCalendarHoursBinary(cfg->regs);
91 timeptr->tm_mday = DL_RTC_Common_getCalendarDayOfMonthBinary(cfg->regs);
92 timeptr->tm_mon = DL_RTC_Common_getCalendarMonthBinary(cfg->regs);
93 timeptr->tm_year = DL_RTC_Common_getCalendarYearBinary(cfg->regs);
94 timeptr->tm_wday = DL_RTC_Common_getCalendarDayOfWeekBinary(cfg->regs);
95 timeptr->tm_nsec = 0;
96 timeptr->tm_isdst = -1;
97 }
98
99 return 0;
100 }
101
102 #if defined(CONFIG_RTC_ALARM)
rtc_ti_mspm0_alarm_get_supported_fields(const struct device * dev,uint16_t id,uint16_t * mask)103 static int rtc_ti_mspm0_alarm_get_supported_fields(const struct device *dev,
104 uint16_t id, uint16_t *mask)
105 {
106 ARG_UNUSED(dev);
107
108 if (id != RTC_TI_ALARM_1 && id != RTC_TI_ALARM_2) {
109 return -EINVAL;
110 }
111
112 __ASSERT(mask != NULL, "Invalid argument mask");
113 *mask = (RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR |
114 RTC_ALARM_TIME_MASK_WEEKDAY | RTC_ALARM_TIME_MASK_MONTHDAY);
115
116 return 0;
117 }
118
rtc_ti_mspm0_set_alarm1(const struct device * dev,uint16_t mask,const struct rtc_time * timeptr)119 static inline void rtc_ti_mspm0_set_alarm1(const struct device *dev,
120 uint16_t mask,
121 const struct rtc_time *timeptr)
122 {
123 const struct rtc_ti_mspm0_config *cfg = dev->config;
124
125 DL_RTC_Common_disableInterrupt(cfg->regs, DL_RTC_COMMON_INTERRUPT_CALENDAR_ALARM1);
126
127 if (mask & RTC_ALARM_TIME_MASK_MINUTE) {
128 cfg->regs->A1MIN = 0;
129 DL_RTC_Common_setAlarm1MinutesBinary(cfg->regs, timeptr->tm_min);
130 DL_RTC_Common_enableAlarm1MinutesBinary(cfg->regs);
131 }
132
133 if (mask & RTC_ALARM_TIME_MASK_HOUR) {
134 DL_RTC_Common_setAlarm1HoursBinary(cfg->regs, timeptr->tm_hour);
135 DL_RTC_Common_enableAlarm1HoursBinary(cfg->regs);
136 }
137
138 if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) {
139 DL_RTC_Common_setAlarm1DayOfWeekBinary(cfg->regs, timeptr->tm_wday);
140 DL_RTC_Common_enableAlarm1DayOfWeekBinary(cfg->regs);
141 }
142
143 if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) {
144 DL_RTC_Common_setAlarm1DayOfMonthBinary(cfg->regs, timeptr->tm_mday);
145 DL_RTC_Common_enableAlarm1DayOfMonthBinary(cfg->regs);
146 }
147
148 DL_RTC_Common_enableInterrupt(cfg->regs, DL_RTC_COMMON_INTERRUPT_CALENDAR_ALARM1);
149 }
150
rtc_ti_mspm0_set_alarm2(const struct device * dev,uint16_t mask,const struct rtc_time * timeptr)151 static inline void rtc_ti_mspm0_set_alarm2(const struct device *dev,
152 uint16_t mask,
153 const struct rtc_time *timeptr)
154 {
155 const struct rtc_ti_mspm0_config *cfg = dev->config;
156
157 DL_RTC_Common_disableInterrupt(cfg->regs, DL_RTC_COMMON_INTERRUPT_CALENDAR_ALARM2);
158
159 if (mask & RTC_ALARM_TIME_MASK_MINUTE) {
160 cfg->regs->A2MIN = 0;
161 DL_RTC_Common_setAlarm2MinutesBinary(cfg->regs, timeptr->tm_min);
162 DL_RTC_Common_enableAlarm2MinutesBinary(cfg->regs);
163 }
164
165 if (mask & RTC_ALARM_TIME_MASK_HOUR) {
166 DL_RTC_Common_setAlarm2HoursBinary(cfg->regs, timeptr->tm_hour);
167 DL_RTC_Common_enableAlarm2HoursBinary(cfg->regs);
168 }
169
170 if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) {
171 DL_RTC_Common_setAlarm2DayOfWeekBinary(cfg->regs, timeptr->tm_wday);
172 DL_RTC_Common_enableAlarm2DayOfWeekBinary(cfg->regs);
173 }
174
175 if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) {
176 DL_RTC_Common_setAlarm2DayOfMonthBinary(cfg->regs, timeptr->tm_mday);
177 DL_RTC_Common_enableAlarm2DayOfMonthBinary(cfg->regs);
178 }
179
180 DL_RTC_Common_enableInterrupt(cfg->regs, DL_RTC_COMMON_INTERRUPT_CALENDAR_ALARM2);
181 }
182
rtc_ti_mspm0_clear_alarm(const struct device * dev,uint16_t id)183 static inline void rtc_ti_mspm0_clear_alarm(const struct device *dev, uint16_t id)
184 {
185 const struct rtc_ti_mspm0_config *cfg = dev->config;
186
187 if (id == RTC_TI_ALARM_1) {
188 cfg->regs->A1MIN = 0x00;
189 cfg->regs->A1HOUR = 0x00;
190 cfg->regs->A1DAY = 0x00;
191 } else {
192 cfg->regs->A2MIN = 0x00;
193 cfg->regs->A2HOUR = 0x00;
194 cfg->regs->A2DAY = 0x00;
195 }
196 }
197
rtc_ti_mspm0_alarm_set_time(const struct device * dev,uint16_t id,uint16_t mask,const struct rtc_time * timeptr)198 static int rtc_ti_mspm0_alarm_set_time(const struct device *dev, uint16_t id,
199 uint16_t mask,
200 const struct rtc_time *timeptr)
201 {
202 struct rtc_ti_mspm0_data *data = dev->data;
203
204 if (timeptr == NULL) {
205 return -EINVAL;
206 }
207
208 if (id != RTC_TI_ALARM_1 && id != RTC_TI_ALARM_2) {
209 return -EINVAL;
210 }
211
212 if (!rtc_utils_validate_rtc_time(timeptr, mask)) {
213 return -EINVAL;
214 }
215
216 K_SPINLOCK(&data->lock) {
217 rtc_ti_mspm0_clear_alarm(dev, id);
218
219 if (id == RTC_TI_ALARM_1) {
220 rtc_ti_mspm0_set_alarm1(dev, mask, timeptr);
221 } else {
222 rtc_ti_mspm0_set_alarm2(dev, mask, timeptr);
223 }
224
225 data->rtc_alarm[id].mask = mask;
226 data->rtc_alarm[id].is_pending = false;
227 }
228
229 return 0;
230 }
231
rtc_ti_mspm0_get_alarm1(const struct device * dev,struct rtc_time * timeptr)232 static int rtc_ti_mspm0_get_alarm1(const struct device *dev,
233 struct rtc_time *timeptr)
234 {
235 uint16_t return_mask = 0;
236 uint16_t alarm_mask = 0;
237 const struct rtc_ti_mspm0_config *cfg = dev->config;
238 struct rtc_ti_mspm0_data *data = dev->data;
239
240 alarm_mask = data->rtc_alarm[RTC_TI_ALARM_1].mask;
241 if (alarm_mask & RTC_ALARM_TIME_MASK_MINUTE) {
242 timeptr->tm_min = DL_RTC_Common_getAlarm1MinutesBinary(cfg->regs);
243 return_mask |= RTC_ALARM_TIME_MASK_MINUTE;
244 }
245
246 if (alarm_mask & RTC_ALARM_TIME_MASK_HOUR) {
247 timeptr->tm_hour = DL_RTC_Common_getAlarm1HoursBinary(cfg->regs);
248 return_mask |= RTC_ALARM_TIME_MASK_HOUR;
249 }
250
251 if (alarm_mask & RTC_ALARM_TIME_MASK_WEEKDAY) {
252 timeptr->tm_wday = DL_RTC_Common_getAlarm1DayOfWeekBinary(cfg->regs);
253 return_mask |= RTC_ALARM_TIME_MASK_WEEKDAY;
254 }
255
256 if (alarm_mask & RTC_ALARM_TIME_MASK_MONTHDAY) {
257 timeptr->tm_mday = DL_RTC_Common_getAlarm1DayOfMonthBinary(cfg->regs);
258 return_mask |= RTC_ALARM_TIME_MASK_MONTHDAY;
259 }
260
261 return return_mask;
262 }
263
rtc_ti_mspm0_get_alarm2(const struct device * dev,struct rtc_time * timeptr)264 static int rtc_ti_mspm0_get_alarm2(const struct device *dev, struct rtc_time *timeptr)
265 {
266 uint16_t return_mask = 0;
267 uint16_t alarm_mask = 0;
268 const struct rtc_ti_mspm0_config *cfg = dev->config;
269 struct rtc_ti_mspm0_data *data = dev->data;
270
271 alarm_mask = data->rtc_alarm[RTC_TI_ALARM_2].mask;
272 if (alarm_mask & RTC_ALARM_TIME_MASK_MINUTE) {
273 timeptr->tm_min = DL_RTC_Common_getAlarm2MinutesBinary(cfg->regs);
274 return_mask |= RTC_ALARM_TIME_MASK_MINUTE;
275 }
276
277 if (alarm_mask & RTC_ALARM_TIME_MASK_HOUR) {
278 timeptr->tm_hour = DL_RTC_Common_getAlarm2HoursBinary(cfg->regs);
279 return_mask |= RTC_ALARM_TIME_MASK_HOUR;
280 }
281
282 if (alarm_mask & RTC_ALARM_TIME_MASK_WEEKDAY) {
283 timeptr->tm_wday = DL_RTC_Common_getAlarm2DayOfWeekBinary(cfg->regs);
284 return_mask |= RTC_ALARM_TIME_MASK_WEEKDAY;
285 }
286
287 if (alarm_mask & RTC_ALARM_TIME_MASK_MONTHDAY) {
288 timeptr->tm_mday = DL_RTC_Common_getAlarm2DayOfMonthBinary(cfg->regs);
289 return_mask |= RTC_ALARM_TIME_MASK_MONTHDAY;
290 }
291
292 return return_mask;
293 }
294
rtc_ti_mspm0_alarm_get_time(const struct device * dev,uint16_t id,uint16_t * mask,struct rtc_time * timeptr)295 static int rtc_ti_mspm0_alarm_get_time(const struct device *dev, uint16_t id,
296 uint16_t *mask, struct rtc_time *timeptr)
297 {
298 struct rtc_ti_mspm0_data *data = dev->data;
299
300 if (timeptr == NULL) {
301 return -EINVAL;
302 }
303
304 if (id != RTC_TI_ALARM_1 && id != RTC_TI_ALARM_2) {
305 return -EINVAL;
306 }
307
308 K_SPINLOCK(&data->lock) {
309 if (id == RTC_TI_ALARM_1) {
310 *mask = rtc_ti_mspm0_get_alarm1(dev, timeptr);
311 } else {
312 *mask = rtc_ti_mspm0_get_alarm2(dev, timeptr);
313 }
314 }
315
316 return 0;
317 }
318
rtc_ti_mspm0_alarm_set_callback(const struct device * dev,uint16_t id,rtc_alarm_callback callback,void * user_data)319 static int rtc_ti_mspm0_alarm_set_callback(const struct device *dev, uint16_t id,
320 rtc_alarm_callback callback,
321 void *user_data)
322 {
323 struct rtc_ti_mspm0_data *data = dev->data;
324
325 if (callback == NULL) {
326 return -EINVAL;
327 }
328
329 if (id != RTC_TI_ALARM_1 && id != RTC_TI_ALARM_2) {
330 return -EINVAL;
331 }
332
333 K_SPINLOCK(&data->lock) {
334 data->rtc_alarm[id].callback = callback;
335 data->rtc_alarm[id].user_data = user_data;
336 }
337
338 return 0;
339 }
340
rtc_ti_mspm0_alarm_is_pending(const struct device * dev,uint16_t id)341 static int rtc_ti_mspm0_alarm_is_pending(const struct device *dev, uint16_t id)
342 {
343 int ret;
344 struct rtc_ti_mspm0_alarm *alarm = NULL;
345 struct rtc_ti_mspm0_data *data = dev->data;
346
347 if (id != RTC_TI_ALARM_1 && id != RTC_TI_ALARM_2) {
348 return -EINVAL;
349 }
350
351 k_spinlock_key_t key = k_spin_lock(&data->lock);
352
353 alarm = &data->rtc_alarm[id];
354 ret = alarm->is_pending ? 1 : 0;
355 alarm->is_pending = false;
356
357 k_spin_unlock(&data->lock, key);
358 return ret;
359 }
360
rtc_ti_mspm0_isr(const struct device * dev)361 static void rtc_ti_mspm0_isr(const struct device *dev)
362 {
363 uint8_t id;
364 struct rtc_ti_mspm0_alarm *alarm = NULL;
365 const struct rtc_ti_mspm0_config *cfg = dev->config;
366 struct rtc_ti_mspm0_data *data = dev->data;
367 k_spinlock_key_t key = k_spin_lock(&data->lock);
368
369 switch (DL_RTC_Common_getPendingInterrupt(cfg->regs)) {
370 case DL_RTC_COMMON_IIDX_ALARM1:
371 id = RTC_TI_ALARM_1;
372 alarm = &data->rtc_alarm[RTC_TI_ALARM_1];
373 break;
374 case DL_RTC_COMMON_IIDX_ALARM2:
375 id = RTC_TI_ALARM_2;
376 alarm = &data->rtc_alarm[RTC_TI_ALARM_2];
377 break;
378 default:
379 goto out;
380 }
381
382 if (alarm != NULL) {
383 alarm->is_pending = true;
384 if (alarm->callback) {
385 alarm->callback(dev, id, alarm->user_data);
386 }
387 }
388
389 out:
390 k_spin_unlock(&data->lock, key);
391 }
392 #endif
393
rtc_ti_mspm0_init(const struct device * dev)394 static int rtc_ti_mspm0_init(const struct device *dev)
395 {
396 const struct rtc_ti_mspm0_config *cfg = dev->config;
397
398 if (!cfg->rtc_x) {
399 /* Enable power to RTC module */
400 if (!DL_RTC_Common_isPowerEnabled(cfg->regs)) {
401 DL_RTC_Common_enablePower(cfg->regs);
402 }
403 }
404
405 DL_RTC_Common_enableClockControl(cfg->regs);
406 DL_RTC_Common_setClockFormat(cfg->regs, DL_RTC_COMMON_FORMAT_BINARY);
407
408 #if defined(CONFIG_RTC_ALARM)
409 cfg->irq_config_func();
410 #endif
411
412 return 0;
413 }
414
415 static DEVICE_API(rtc, rtc_ti_mspm0_driver_api) = {
416 .set_time = rtc_ti_mspm0_set_time,
417 .get_time = rtc_ti_mspm0_get_time,
418 #if defined(CONFIG_RTC_ALARM)
419 .alarm_set_time = rtc_ti_mspm0_alarm_set_time,
420 .alarm_get_time = rtc_ti_mspm0_alarm_get_time,
421 .alarm_is_pending = rtc_ti_mspm0_alarm_is_pending,
422 .alarm_set_callback = rtc_ti_mspm0_alarm_set_callback,
423 .alarm_get_supported_fields = rtc_ti_mspm0_alarm_get_supported_fields,
424 #endif /* CONFIG_RTC_ALARM */
425 };
426
427 #define RTC_TI_MSPM0_DEVICE_INIT(n) \
428 IF_ENABLED(CONFIG_RTC_ALARM, \
429 (static void ti_mspm0_config_irq_##n(void) \
430 { \
431 IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \
432 rtc_ti_mspm0_isr, DEVICE_DT_INST_GET(n), 0); \
433 irq_enable(DT_INST_IRQN(n)); \
434 })) \
435 \
436 static struct rtc_ti_mspm0_data rtc_data_##n; \
437 \
438 static struct rtc_ti_mspm0_config rtc_config_##n = { \
439 .regs = (RTC_Regs *)DT_INST_REG_ADDR(n), \
440 .rtc_x = DT_INST_PROP(n, ti_rtc_x), \
441 IF_ENABLED(CONFIG_RTC_ALARM, \
442 (.irq_config_func = ti_mspm0_config_irq_##n,)) \
443 }; \
444 \
445 DEVICE_DT_INST_DEFINE(n, &rtc_ti_mspm0_init, NULL, &rtc_data_##n, \
446 &rtc_config_##n, PRE_KERNEL_1, \
447 CONFIG_RTC_INIT_PRIORITY, &rtc_ti_mspm0_driver_api);
448
449 DT_INST_FOREACH_STATUS_OKAY(RTC_TI_MSPM0_DEVICE_INIT);
450