/* * SPDX-License-Identifier: Apache-2.0 * * Copyright (c) 2024 Andriy Gelman * Author: Andriy Gelman */ #include #include #include #include #include #include #include #define DT_DRV_COMPAT infineon_xmc4xxx_rtc #define RTC_XMC4XXX_DEFAULT_PRESCALER 0x7fff #define RTC_XMC4XXX_SUPPORTED_ALARM_MASK \ (RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR | \ RTC_ALARM_TIME_MASK_MONTHDAY | RTC_ALARM_TIME_MASK_MONTH | RTC_ALARM_TIME_MASK_YEAR) struct rtc_xmc4xxx_data { #if defined(CONFIG_RTC_ALARM) rtc_alarm_callback alarm_callback; void *alarm_user_data; #endif #if defined(CONFIG_RTC_UPDATE) rtc_update_callback update_callback; void *update_user_data; #endif }; static int rtc_xmc4xxx_set_time(const struct device *dev, const struct rtc_time *timeptr) { const struct tm *stdtime; if (timeptr == NULL) { return -EINVAL; } XMC_RTC_Stop(); stdtime = rtc_time_to_tm((struct rtc_time *)timeptr); XMC_RTC_SetTimeStdFormat(stdtime); XMC_RTC_Start(); return 0; } static int rtc_xmc4xxx_get_time(const struct device *dev, struct rtc_time *timeptr) { struct tm *stdtime = rtc_time_to_tm(timeptr); if (!XMC_RTC_IsRunning()) { return -ENODATA; } if (stdtime == NULL) { return -EINVAL; } XMC_RTC_GetTimeStdFormat(stdtime); timeptr->tm_nsec = 0; return 0; } #if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) static void rtc_xmc4xxx_isr(const struct device *dev) { struct rtc_xmc4xxx_data *dev_data = dev->data; uint32_t event = SCU_INTERRUPT->SRRAW; #if defined(CONFIG_RTC_ALARM) if ((event & XMC_SCU_INTERRUPT_EVENT_RTC_ALARM) != 0) { if (dev_data->alarm_callback != NULL) { dev_data->alarm_callback(dev, 0, dev_data->alarm_user_data); } XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM); } #endif #if defined(CONFIG_RTC_UPDATE) if ((event & XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC) != 0) { if (dev_data->update_callback != NULL) { dev_data->update_callback(dev, dev_data->update_user_data); } XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC); } #endif } #endif #ifdef CONFIG_RTC_ALARM static int rtc_xmc4xxx_alarm_get_supported_fields(const struct device *dev, uint16_t id, uint16_t *mask) { ARG_UNUSED(dev); ARG_UNUSED(id); *mask = RTC_XMC4XXX_SUPPORTED_ALARM_MASK; return 0; } static int rtc_xmc4xxx_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask, const struct rtc_time *timeptr) { const struct tm *stdtime = rtc_time_to_tm((struct rtc_time *)timeptr); if (id != 0 || (mask > 0 && timeptr == NULL)) { return -EINVAL; } if (mask == 0) { XMC_RTC_DisableEvent(XMC_RTC_EVENT_ALARM); XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM); return 0; } if (mask != RTC_XMC4XXX_SUPPORTED_ALARM_MASK) { return -EINVAL; } XMC_RTC_SetAlarmStdFormat(stdtime); XMC_RTC_EnableEvent(XMC_RTC_EVENT_ALARM); return 0; } static int rtc_xmc4xxx_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask, struct rtc_time *timeptr) { ARG_UNUSED(dev); struct tm *stdtime = rtc_time_to_tm(timeptr); if (id != 0 || mask == NULL || timeptr == NULL) { return -EINVAL; } *mask = RTC_XMC4XXX_SUPPORTED_ALARM_MASK; XMC_RTC_GetAlarmStdFormat(stdtime); return 0; } static int rtc_xmc4xxx_alarm_is_pending(const struct device *dev, uint16_t id) { ARG_UNUSED(dev); unsigned int key; int alarm = 0; uint32_t event; if (id != 0) { return -EINVAL; } key = irq_lock(); event = SCU_INTERRUPT->SRRAW; if ((event & XMC_SCU_INTERRUPT_EVENT_RTC_ALARM) != 0) { alarm = 1; XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM); } irq_unlock(key); return alarm; } static int rtc_xmc4xxx_alarm_set_callback(const struct device *dev, uint16_t id, rtc_alarm_callback callback, void *user_data) { struct rtc_xmc4xxx_data *dev_data = dev->data; unsigned int key; if (id != 0) { return -EINVAL; } key = irq_lock(); dev_data->alarm_callback = callback; dev_data->alarm_user_data = user_data; irq_unlock(key); if (dev_data->alarm_callback) { XMC_SCU_INTERRUPT_EnableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM); } else { XMC_SCU_INTERRUPT_DisableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM); } return 0; } #endif /* CONFIG_RTC_ALARM */ #ifdef CONFIG_RTC_UPDATE static int rtc_xmc4xxx_update_set_callback(const struct device *dev, rtc_update_callback callback, void *user_data) { struct rtc_xmc4xxx_data *dev_data = dev->data; unsigned int key; key = irq_lock(); dev_data->update_callback = callback; dev_data->update_user_data = user_data; irq_unlock(key); if (dev_data->update_callback) { XMC_RTC_EnableEvent(XMC_RTC_EVENT_PERIODIC_SECONDS); XMC_SCU_INTERRUPT_EnableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC); } else { XMC_SCU_INTERRUPT_DisableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC); XMC_RTC_DisableEvent(XMC_RTC_EVENT_PERIODIC_SECONDS); } return 0; } #endif /* CONFIG_RTC_UPDATE */ static DEVICE_API(rtc, rtc_xmc4xxx_driver_api) = { .set_time = rtc_xmc4xxx_set_time, .get_time = rtc_xmc4xxx_get_time, #ifdef CONFIG_RTC_ALARM .alarm_get_supported_fields = rtc_xmc4xxx_alarm_get_supported_fields, .alarm_set_time = rtc_xmc4xxx_alarm_set_time, .alarm_get_time = rtc_xmc4xxx_alarm_get_time, .alarm_is_pending = rtc_xmc4xxx_alarm_is_pending, .alarm_set_callback = rtc_xmc4xxx_alarm_set_callback, #endif #ifdef CONFIG_RTC_UPDATE .update_set_callback = rtc_xmc4xxx_update_set_callback, #endif }; #if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) static void rtc_xmc4xxx_irq_config(void) { /* RTC and watchdog share the same interrupt. Shared interrupts must */ /* be enabled if WDT is enabled and RTC is using alarm or update feature */ IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), rtc_xmc4xxx_isr, DEVICE_DT_INST_GET(0), 0); irq_enable(DT_INST_IRQN(0)); } #endif static int rtc_xmc4xxx_init(const struct device *dev) { if (!XMC_RTC_IsRunning()) { if (!XMC_SCU_HIB_IsHibernateDomainEnabled()) { XMC_SCU_HIB_EnableHibernateDomain(); } XMC_RTC_SetPrescaler(RTC_XMC4XXX_DEFAULT_PRESCALER); } #if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) rtc_xmc4xxx_irq_config(); #endif return 0; } static struct rtc_xmc4xxx_data rtc_xmc4xxx_data_0; DEVICE_DT_INST_DEFINE(0, rtc_xmc4xxx_init, NULL, &rtc_xmc4xxx_data_0, NULL, POST_KERNEL, CONFIG_RTC_INIT_PRIORITY, &rtc_xmc4xxx_driver_api);