blob: 3a02ab6331e2d600c59ce780574f2acc3e58aa7f [file] [log] [blame]
/*
* Copyright (c) 2025 Linumiz GmbH
*
* SPDX-License-Identifier: Apache-2.0
*
*/
#define DT_DRV_COMPAT ti_mspm0_rtc
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/rtc.h>
#include <zephyr/init.h>
#include <zephyr/irq.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/util_macro.h>
#include <zephyr/sys/__assert.h>
#include "rtc_utils.h"
#include <ti/driverlib/dl_rtc_common.h>
#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);