/* * Copyright (c) 2025 Linumiz GmbH * * SPDX-License-Identifier: Apache-2.0 * */ #define DT_DRV_COMPAT ti_mspm0_rtc #include #include #include #include #include #include #include #include #include "rtc_utils.h" #include #if defined(CONFIG_RTC_ALARM) #define RTC_TI_ALARM_1 0 #define RTC_TI_ALARM_2 1 #define RTC_TI_MAX_ALARM DT_INST_PROP(0, alarms_count) BUILD_ASSERT((RTC_TI_MAX_ALARM != 0), "CONFIG_RTC_ALARM is enabled, without setting alarms-count property"); #endif struct rtc_ti_mspm0_config { RTC_Regs *regs; #if defined(CONFIG_RTC_ALARM) void (*irq_config_func)(void); #endif bool rtc_x; }; #if defined(CONFIG_RTC_ALARM) struct rtc_ti_mspm0_alarm { rtc_alarm_callback callback; void *user_data; uint16_t mask; bool is_pending; }; #endif struct rtc_ti_mspm0_data { struct k_spinlock lock; #if defined(CONFIG_RTC_ALARM) struct rtc_ti_mspm0_alarm rtc_alarm[RTC_TI_MAX_ALARM]; #endif }; static int rtc_ti_mspm0_set_time(const struct device *dev, const struct rtc_time *timeptr) { const struct rtc_ti_mspm0_config *cfg = dev->config; struct rtc_ti_mspm0_data *data = dev->data; if ((timeptr == NULL) || !rtc_utils_validate_rtc_time(timeptr, 0)) { return -EINVAL; } K_SPINLOCK(&data->lock) { DL_RTC_Common_setCalendarSecondsBinary(cfg->regs, timeptr->tm_sec); DL_RTC_Common_setCalendarMinutesBinary(cfg->regs, timeptr->tm_min); DL_RTC_Common_setCalendarHoursBinary(cfg->regs, timeptr->tm_hour); DL_RTC_Common_setCalendarDayOfWeekBinary(cfg->regs, timeptr->tm_wday); DL_RTC_Common_setCalendarDayOfMonthBinary(cfg->regs, timeptr->tm_mday); DL_RTC_Common_setCalendarMonthBinary(cfg->regs, timeptr->tm_mon); DL_RTC_Common_setCalendarYearBinary(cfg->regs, timeptr->tm_year); } return 0; } static int rtc_ti_mspm0_get_time(const struct device *dev, struct rtc_time *timeptr) { const struct rtc_ti_mspm0_config *cfg = dev->config; struct rtc_ti_mspm0_data *data = dev->data; if (timeptr == NULL) { return -EINVAL; } K_SPINLOCK(&data->lock) { timeptr->tm_sec = DL_RTC_Common_getCalendarSecondsBinary(cfg->regs); timeptr->tm_min = DL_RTC_Common_getCalendarMinutesBinary(cfg->regs); timeptr->tm_hour = DL_RTC_Common_getCalendarHoursBinary(cfg->regs); timeptr->tm_mday = DL_RTC_Common_getCalendarDayOfMonthBinary(cfg->regs); timeptr->tm_mon = DL_RTC_Common_getCalendarMonthBinary(cfg->regs); timeptr->tm_year = DL_RTC_Common_getCalendarYearBinary(cfg->regs); timeptr->tm_wday = DL_RTC_Common_getCalendarDayOfWeekBinary(cfg->regs); timeptr->tm_nsec = 0; timeptr->tm_isdst = -1; } return 0; } #if defined(CONFIG_RTC_ALARM) static int rtc_ti_mspm0_alarm_get_supported_fields(const struct device *dev, uint16_t id, uint16_t *mask) { ARG_UNUSED(dev); if (id != RTC_TI_ALARM_1 && id != RTC_TI_ALARM_2) { return -EINVAL; } __ASSERT(mask != NULL, "Invalid argument mask"); *mask = (RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR | RTC_ALARM_TIME_MASK_WEEKDAY | RTC_ALARM_TIME_MASK_MONTHDAY); return 0; } static inline void rtc_ti_mspm0_set_alarm1(const struct device *dev, uint16_t mask, const struct rtc_time *timeptr) { const struct rtc_ti_mspm0_config *cfg = dev->config; DL_RTC_Common_disableInterrupt(cfg->regs, DL_RTC_COMMON_INTERRUPT_CALENDAR_ALARM1); if (mask & RTC_ALARM_TIME_MASK_MINUTE) { cfg->regs->A1MIN = 0; DL_RTC_Common_setAlarm1MinutesBinary(cfg->regs, timeptr->tm_min); DL_RTC_Common_enableAlarm1MinutesBinary(cfg->regs); } if (mask & RTC_ALARM_TIME_MASK_HOUR) { DL_RTC_Common_setAlarm1HoursBinary(cfg->regs, timeptr->tm_hour); DL_RTC_Common_enableAlarm1HoursBinary(cfg->regs); } if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) { DL_RTC_Common_setAlarm1DayOfWeekBinary(cfg->regs, timeptr->tm_wday); DL_RTC_Common_enableAlarm1DayOfWeekBinary(cfg->regs); } if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { DL_RTC_Common_setAlarm1DayOfMonthBinary(cfg->regs, timeptr->tm_mday); DL_RTC_Common_enableAlarm1DayOfMonthBinary(cfg->regs); } DL_RTC_Common_enableInterrupt(cfg->regs, DL_RTC_COMMON_INTERRUPT_CALENDAR_ALARM1); } static inline void rtc_ti_mspm0_set_alarm2(const struct device *dev, uint16_t mask, const struct rtc_time *timeptr) { const struct rtc_ti_mspm0_config *cfg = dev->config; DL_RTC_Common_disableInterrupt(cfg->regs, DL_RTC_COMMON_INTERRUPT_CALENDAR_ALARM2); if (mask & RTC_ALARM_TIME_MASK_MINUTE) { cfg->regs->A2MIN = 0; DL_RTC_Common_setAlarm2MinutesBinary(cfg->regs, timeptr->tm_min); DL_RTC_Common_enableAlarm2MinutesBinary(cfg->regs); } if (mask & RTC_ALARM_TIME_MASK_HOUR) { DL_RTC_Common_setAlarm2HoursBinary(cfg->regs, timeptr->tm_hour); DL_RTC_Common_enableAlarm2HoursBinary(cfg->regs); } if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) { DL_RTC_Common_setAlarm2DayOfWeekBinary(cfg->regs, timeptr->tm_wday); DL_RTC_Common_enableAlarm2DayOfWeekBinary(cfg->regs); } if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { DL_RTC_Common_setAlarm2DayOfMonthBinary(cfg->regs, timeptr->tm_mday); DL_RTC_Common_enableAlarm2DayOfMonthBinary(cfg->regs); } DL_RTC_Common_enableInterrupt(cfg->regs, DL_RTC_COMMON_INTERRUPT_CALENDAR_ALARM2); } static inline void rtc_ti_mspm0_clear_alarm(const struct device *dev, uint16_t id) { const struct rtc_ti_mspm0_config *cfg = dev->config; if (id == RTC_TI_ALARM_1) { cfg->regs->A1MIN = 0x00; cfg->regs->A1HOUR = 0x00; cfg->regs->A1DAY = 0x00; } else { cfg->regs->A2MIN = 0x00; cfg->regs->A2HOUR = 0x00; cfg->regs->A2DAY = 0x00; } } static int rtc_ti_mspm0_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask, const struct rtc_time *timeptr) { struct rtc_ti_mspm0_data *data = dev->data; if (timeptr == NULL) { return -EINVAL; } if (id != RTC_TI_ALARM_1 && id != RTC_TI_ALARM_2) { return -EINVAL; } if (!rtc_utils_validate_rtc_time(timeptr, mask)) { return -EINVAL; } K_SPINLOCK(&data->lock) { rtc_ti_mspm0_clear_alarm(dev, id); if (id == RTC_TI_ALARM_1) { rtc_ti_mspm0_set_alarm1(dev, mask, timeptr); } else { rtc_ti_mspm0_set_alarm2(dev, mask, timeptr); } data->rtc_alarm[id].mask = mask; data->rtc_alarm[id].is_pending = false; } return 0; } static int rtc_ti_mspm0_get_alarm1(const struct device *dev, struct rtc_time *timeptr) { uint16_t return_mask = 0; uint16_t alarm_mask = 0; const struct rtc_ti_mspm0_config *cfg = dev->config; struct rtc_ti_mspm0_data *data = dev->data; alarm_mask = data->rtc_alarm[RTC_TI_ALARM_1].mask; if (alarm_mask & RTC_ALARM_TIME_MASK_MINUTE) { timeptr->tm_min = DL_RTC_Common_getAlarm1MinutesBinary(cfg->regs); return_mask |= RTC_ALARM_TIME_MASK_MINUTE; } if (alarm_mask & RTC_ALARM_TIME_MASK_HOUR) { timeptr->tm_hour = DL_RTC_Common_getAlarm1HoursBinary(cfg->regs); return_mask |= RTC_ALARM_TIME_MASK_HOUR; } if (alarm_mask & RTC_ALARM_TIME_MASK_WEEKDAY) { timeptr->tm_wday = DL_RTC_Common_getAlarm1DayOfWeekBinary(cfg->regs); return_mask |= RTC_ALARM_TIME_MASK_WEEKDAY; } if (alarm_mask & RTC_ALARM_TIME_MASK_MONTHDAY) { timeptr->tm_mday = DL_RTC_Common_getAlarm1DayOfMonthBinary(cfg->regs); return_mask |= RTC_ALARM_TIME_MASK_MONTHDAY; } return return_mask; } static int rtc_ti_mspm0_get_alarm2(const struct device *dev, struct rtc_time *timeptr) { uint16_t return_mask = 0; uint16_t alarm_mask = 0; const struct rtc_ti_mspm0_config *cfg = dev->config; struct rtc_ti_mspm0_data *data = dev->data; alarm_mask = data->rtc_alarm[RTC_TI_ALARM_2].mask; if (alarm_mask & RTC_ALARM_TIME_MASK_MINUTE) { timeptr->tm_min = DL_RTC_Common_getAlarm2MinutesBinary(cfg->regs); return_mask |= RTC_ALARM_TIME_MASK_MINUTE; } if (alarm_mask & RTC_ALARM_TIME_MASK_HOUR) { timeptr->tm_hour = DL_RTC_Common_getAlarm2HoursBinary(cfg->regs); return_mask |= RTC_ALARM_TIME_MASK_HOUR; } if (alarm_mask & RTC_ALARM_TIME_MASK_WEEKDAY) { timeptr->tm_wday = DL_RTC_Common_getAlarm2DayOfWeekBinary(cfg->regs); return_mask |= RTC_ALARM_TIME_MASK_WEEKDAY; } if (alarm_mask & RTC_ALARM_TIME_MASK_MONTHDAY) { timeptr->tm_mday = DL_RTC_Common_getAlarm2DayOfMonthBinary(cfg->regs); return_mask |= RTC_ALARM_TIME_MASK_MONTHDAY; } return return_mask; } static int rtc_ti_mspm0_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask, struct rtc_time *timeptr) { struct rtc_ti_mspm0_data *data = dev->data; if (timeptr == NULL) { return -EINVAL; } if (id != RTC_TI_ALARM_1 && id != RTC_TI_ALARM_2) { return -EINVAL; } K_SPINLOCK(&data->lock) { if (id == RTC_TI_ALARM_1) { *mask = rtc_ti_mspm0_get_alarm1(dev, timeptr); } else { *mask = rtc_ti_mspm0_get_alarm2(dev, timeptr); } } return 0; } static int rtc_ti_mspm0_alarm_set_callback(const struct device *dev, uint16_t id, rtc_alarm_callback callback, void *user_data) { struct rtc_ti_mspm0_data *data = dev->data; if (callback == NULL) { return -EINVAL; } if (id != RTC_TI_ALARM_1 && id != RTC_TI_ALARM_2) { return -EINVAL; } K_SPINLOCK(&data->lock) { data->rtc_alarm[id].callback = callback; data->rtc_alarm[id].user_data = user_data; } return 0; } static int rtc_ti_mspm0_alarm_is_pending(const struct device *dev, uint16_t id) { int ret; struct rtc_ti_mspm0_alarm *alarm = NULL; struct rtc_ti_mspm0_data *data = dev->data; if (id != RTC_TI_ALARM_1 && id != RTC_TI_ALARM_2) { return -EINVAL; } k_spinlock_key_t key = k_spin_lock(&data->lock); alarm = &data->rtc_alarm[id]; ret = alarm->is_pending ? 1 : 0; alarm->is_pending = false; k_spin_unlock(&data->lock, key); return ret; } static void rtc_ti_mspm0_isr(const struct device *dev) { uint8_t id; struct rtc_ti_mspm0_alarm *alarm = NULL; const struct rtc_ti_mspm0_config *cfg = dev->config; struct rtc_ti_mspm0_data *data = dev->data; k_spinlock_key_t key = k_spin_lock(&data->lock); switch (DL_RTC_Common_getPendingInterrupt(cfg->regs)) { case DL_RTC_COMMON_IIDX_ALARM1: id = RTC_TI_ALARM_1; alarm = &data->rtc_alarm[RTC_TI_ALARM_1]; break; case DL_RTC_COMMON_IIDX_ALARM2: id = RTC_TI_ALARM_2; alarm = &data->rtc_alarm[RTC_TI_ALARM_2]; break; default: goto out; } if (alarm != NULL) { alarm->is_pending = true; if (alarm->callback) { alarm->callback(dev, id, alarm->user_data); } } out: k_spin_unlock(&data->lock, key); } #endif static int rtc_ti_mspm0_init(const struct device *dev) { const struct rtc_ti_mspm0_config *cfg = dev->config; if (!cfg->rtc_x) { /* Enable power to RTC module */ if (!DL_RTC_Common_isPowerEnabled(cfg->regs)) { DL_RTC_Common_enablePower(cfg->regs); } } DL_RTC_Common_enableClockControl(cfg->regs); DL_RTC_Common_setClockFormat(cfg->regs, DL_RTC_COMMON_FORMAT_BINARY); #if defined(CONFIG_RTC_ALARM) cfg->irq_config_func(); #endif return 0; } static DEVICE_API(rtc, rtc_ti_mspm0_driver_api) = { .set_time = rtc_ti_mspm0_set_time, .get_time = rtc_ti_mspm0_get_time, #if defined(CONFIG_RTC_ALARM) .alarm_set_time = rtc_ti_mspm0_alarm_set_time, .alarm_get_time = rtc_ti_mspm0_alarm_get_time, .alarm_is_pending = rtc_ti_mspm0_alarm_is_pending, .alarm_set_callback = rtc_ti_mspm0_alarm_set_callback, .alarm_get_supported_fields = rtc_ti_mspm0_alarm_get_supported_fields, #endif /* CONFIG_RTC_ALARM */ }; #define RTC_TI_MSPM0_DEVICE_INIT(n) \ IF_ENABLED(CONFIG_RTC_ALARM, \ (static void ti_mspm0_config_irq_##n(void) \ { \ IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ rtc_ti_mspm0_isr, DEVICE_DT_INST_GET(n), 0); \ irq_enable(DT_INST_IRQN(n)); \ })) \ \ static struct rtc_ti_mspm0_data rtc_data_##n; \ \ static struct rtc_ti_mspm0_config rtc_config_##n = { \ .regs = (RTC_Regs *)DT_INST_REG_ADDR(n), \ .rtc_x = DT_INST_PROP(n, ti_rtc_x), \ IF_ENABLED(CONFIG_RTC_ALARM, \ (.irq_config_func = ti_mspm0_config_irq_##n,)) \ }; \ \ DEVICE_DT_INST_DEFINE(n, &rtc_ti_mspm0_init, NULL, &rtc_data_##n, \ &rtc_config_##n, PRE_KERNEL_1, \ CONFIG_RTC_INIT_PRIORITY, &rtc_ti_mspm0_driver_api); DT_INST_FOREACH_STATUS_OKAY(RTC_TI_MSPM0_DEVICE_INIT);