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