From 9f76879a0be56b39742240d571f311917132dda1 Mon Sep 17 00:00:00 2001 From: Ioannis Karachalios Date: Mon, 16 Oct 2023 14:57:21 +0300 Subject: [PATCH] drivers: rtc: smartbond: Support RTC peripheral. Add support for the RTC peripheral. Signed-off-by: Ioannis Karachalios --- drivers/rtc/CMakeLists.txt | 1 + drivers/rtc/Kconfig | 1 + drivers/rtc/Kconfig.smartbond | 9 + drivers/rtc/rtc_smartbond.c | 641 ++++++++++++++++++++ dts/bindings/rtc/renesas,smartbond-rtc.yaml | 15 + 5 files changed, 667 insertions(+) create mode 100644 drivers/rtc/Kconfig.smartbond create mode 100644 drivers/rtc/rtc_smartbond.c create mode 100644 dts/bindings/rtc/renesas,smartbond-rtc.yaml diff --git a/drivers/rtc/CMakeLists.txt b/drivers/rtc/CMakeLists.txt index 94cda7627c4..2475174ac68 100644 --- a/drivers/rtc/CMakeLists.txt +++ b/drivers/rtc/CMakeLists.txt @@ -14,3 +14,4 @@ zephyr_library_sources_ifdef(CONFIG_RTC_MOTOROLA_MC146818 rtc_mc146818.c) zephyr_library_sources_ifdef(CONFIG_RTC_STM32 rtc_ll_stm32.c) zephyr_library_sources_ifdef(CONFIG_RTC_SHELL rtc_shell.c) zephyr_library_sources_ifdef(CONFIG_RTC_FAKE rtc_fake.c) +zephyr_library_sources_ifdef(CONFIG_RTC_SMARTBOND rtc_smartbond.c) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 97cc21fef0e..e480aae0388 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -48,5 +48,6 @@ source "drivers/rtc/Kconfig.pcf8523" source "drivers/rtc/Kconfig.pcf8563" source "drivers/rtc/Kconfig.mc146818" source "drivers/rtc/Kconfig.stm32" +source "drivers/rtc/Kconfig.smartbond" endif # RTC diff --git a/drivers/rtc/Kconfig.smartbond b/drivers/rtc/Kconfig.smartbond new file mode 100644 index 00000000000..d21d3a962fa --- /dev/null +++ b/drivers/rtc/Kconfig.smartbond @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config RTC_SMARTBOND + bool "Smartbond RTC driver" + depends on DT_HAS_RENESAS_SMARTBOND_RTC_ENABLED + default y + help + Build RTC driver for Smartbond SoCs. diff --git a/drivers/rtc/rtc_smartbond.c b/drivers/rtc/rtc_smartbond.c new file mode 100644 index 00000000000..a16c8ebaeef --- /dev/null +++ b/drivers/rtc/rtc_smartbond.c @@ -0,0 +1,641 @@ +/* + * Copyright (c) 2023 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(rtc_smartbond, CONFIG_RTC_LOG_LEVEL); + +#define DT_DRV_COMPAT renesas_smartbond_rtc + +#define SMARTBOND_IRQN DT_INST_IRQN(0) +#define SMARTBOND_IRQ_PRIO DT_INST_IRQ(0, priority) + +#define RTC_ALARMS_COUNT DT_PROP(DT_NODELABEL(rtc), alarms_count) + +#define TM_YEAR_REF 1900 +#define RTC_DIV_DENOM_1000 0 +#define RTC_DIV_DENOM_1024 1 + +#define RTC_SMARTBOND_SUPPORTED_ALARM_FIELDS \ + (RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR | \ + RTC_ALARM_TIME_MASK_MONTH | RTC_ALARM_TIME_MASK_MONTHDAY) + +#define RTC_TIME_REG_SET_FIELD(_field, _var, _val) \ + ((_var) = \ + ((_var) & ~(RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _T_Msk | \ + RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _U_Msk)) | \ + (((_val) << RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _U_Pos) & \ + (RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _T_Msk | \ + RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _U_Msk))) + +#define RTC_CALENDAR_REG_SET_FIELD(_field, _var, _val) \ + ((_var) = \ + ((_var) & ~(RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _T_Msk | \ + RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _U_Msk)) | \ + (((_val) << RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _U_Pos) & \ + (RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _T_Msk | \ + RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _U_Msk))) + +#define RTC_CALENDAR_ALARM_REG_SET_FIELD(_field, _var, _val) \ + ((_var) = \ + ((_var) & ~(RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _T_Msk | \ + RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _U_Msk)) | \ + (((_val) << RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _U_Pos) & \ + (RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _T_Msk | \ + RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _U_Msk))) + +#define RTC_TIME_ALARM_REG_SET_FIELD(_field, _var, _val) \ + ((_var) = \ + ((_var) & ~(RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _T_Msk | \ + RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _U_Msk)) | \ + (((_val) << RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _U_Pos) & \ + (RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _T_Msk | \ + RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _U_Msk))) + +#define RTC_TIME_REG_GET_FIELD(_field, _var) \ + (((_var) & (RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _T_Msk | \ + RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _U_Msk)) >> \ + RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _U_Pos) + +#define RTC_CALENDAR_REG_GET_FIELD(_field, _var) \ + (((_var) & (RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _T_Msk | \ + RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _U_Msk)) >> \ + RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _U_Pos) + +#define RTC_CALENDAR_ALARM_REG_GET_FIELD(_field, _var) \ + (((_var) & (RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _T_Msk | \ + RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _U_Msk)) >> \ + RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _U_Pos) + +#define RTC_TIME_ALARM_REG_GET_FIELD(_field, _var) \ + (((_var) & (RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _T_Msk | \ + RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _U_Msk)) >> \ + RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _U_Pos) + +#define CLK_RTCDIV_REG_SET_FIELD(_field, _var, _val) \ + ((_var) = \ + ((_var) & ~CRG_TOP_CLK_RTCDIV_REG_RTC_DIV_ ## _field ## _Msk) | \ + (((_val) << CRG_TOP_CLK_RTCDIV_REG_RTC_DIV_ ## _field ## _Pos) & \ + CRG_TOP_CLK_RTCDIV_REG_RTC_DIV_ ## _field ## _Msk)) + +struct rtc_smartbond_data { + struct k_mutex lock; + bool is_rtc_configured; +#if defined(CONFIG_RTC_ALARM) + volatile bool is_alarm_pending; + rtc_alarm_callback alarm_cb; + void *alarm_user_data; +#endif +#if defined(CONFIG_RTC_UPDATE) + rtc_update_callback update_cb; + void *update_user_data; +#endif +}; + +#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) +static void smartbond_rtc_isr(const struct device *dev) +{ + struct rtc_smartbond_data *data = dev->data; + /* Exercise which events asserted the RTC IRQ line. Register is cleared upon read. */ + uint32_t rtc_event_flags_reg = RTC->RTC_EVENT_FLAGS_REG; + /* RTC_EVENT_FLASH_REG will be updated regardless of the interrupt mask. */ + uint32_t rtc_interrupt_mask_reg = RTC->RTC_INTERRUPT_MASK_REG; + +#if defined(CONFIG_RTC_ALARM) + if ((rtc_event_flags_reg & RTC_RTC_EVENT_FLAGS_REG_RTC_EVENT_ALRM_Msk) && + !(rtc_interrupt_mask_reg & RTC_RTC_INTERRUPT_MASK_REG_RTC_ALRM_INT_MSK_Msk)) { + if (data->alarm_cb) { + data->alarm_cb(dev, 0, data->alarm_user_data); + data->is_alarm_pending = false; + } else { + data->is_alarm_pending = true; + } + } +#endif + +#if defined(CONFIG_RTC_UPDATE) + if ((rtc_event_flags_reg & RTC_RTC_EVENT_FLAGS_REG_RTC_EVENT_SEC_Msk) && + !(rtc_interrupt_mask_reg & RTC_RTC_INTERRUPT_MASK_REG_RTC_SEC_INT_MSK_Msk)) { + if (data->update_cb) { + data->update_cb(dev, data->update_user_data); + } + } +#endif +} +#endif + +static inline void rtc_smartbond_set_status(bool status) +{ + if (status) { + CRG_TOP->CLK_RTCDIV_REG |= CRG_TOP_CLK_RTCDIV_REG_RTC_DIV_ENABLE_Msk; + RTC->RTC_CONTROL_REG = 0; + } else { + RTC->RTC_CONTROL_REG = (RTC_RTC_CONTROL_REG_RTC_CAL_DISABLE_Msk | + RTC_RTC_CONTROL_REG_RTC_TIME_DISABLE_Msk); + CRG_TOP->CLK_RTCDIV_REG &= ~CRG_TOP_CLK_RTCDIV_REG_RTC_DIV_ENABLE_Msk; + } +} + +static uint32_t rtc_time_to_bcd(const struct rtc_time *timeptr) +{ + uint32_t rtc_time_reg = 0; + + RTC_TIME_REG_SET_FIELD(S, rtc_time_reg, bin2bcd(timeptr->tm_sec)); /*[0, 59]*/ + RTC_TIME_REG_SET_FIELD(M, rtc_time_reg, bin2bcd(timeptr->tm_min)); /*[0, 59]*/ + RTC_TIME_REG_SET_FIELD(HR, rtc_time_reg, bin2bcd(timeptr->tm_hour)); /*[0, 23]*/ + + return rtc_time_reg; +} + +static uint32_t rtc_calendar_to_bcd(const struct rtc_time *timeptr) +{ + uint32_t rtc_calendar_reg = 0; + + RTC_CALENDAR_REG_SET_FIELD(D, rtc_calendar_reg, bin2bcd(timeptr->tm_mday)); /*[1, 31]*/ + RTC_CALENDAR_REG_SET_FIELD(Y, rtc_calendar_reg, + bin2bcd((timeptr->tm_year + TM_YEAR_REF) % 100)); /*[year - 1900]*/ + RTC_CALENDAR_REG_SET_FIELD(C, rtc_calendar_reg, + bin2bcd((timeptr->tm_year + TM_YEAR_REF) / 100)); + RTC_CALENDAR_REG_SET_FIELD(M, rtc_calendar_reg, bin2bcd(timeptr->tm_mon + 1)); /*[0, 11]*/ + + if (timeptr->tm_wday != -1) { + rtc_calendar_reg |= ((timeptr->tm_wday + 1) & + RTC_RTC_CALENDAR_REG_RTC_DAY_Msk); /*[0, 6]*/ + } + + return rtc_calendar_reg; +} + +static void bcd_to_rtc_time(struct rtc_time *timeptr) +{ + uint32_t rtc_time_reg = RTC->RTC_TIME_REG; + + timeptr->tm_sec = bcd2bin(RTC_TIME_REG_GET_FIELD(S, rtc_time_reg)); + timeptr->tm_min = bcd2bin(RTC_TIME_REG_GET_FIELD(M, rtc_time_reg)); + timeptr->tm_hour = bcd2bin(RTC_TIME_REG_GET_FIELD(HR, rtc_time_reg)); + + timeptr->tm_nsec = 0; /*Unknown*/ +} + +static void bcd_to_rtc_calendar(struct rtc_time *timeptr) +{ + uint32_t rtc_calendar_reg = RTC->RTC_CALENDAR_REG; + + timeptr->tm_mday = bcd2bin(RTC_CALENDAR_REG_GET_FIELD(D, rtc_calendar_reg)); + timeptr->tm_mon = bcd2bin(RTC_CALENDAR_REG_GET_FIELD(M, rtc_calendar_reg)) - 1; + timeptr->tm_year = bcd2bin(RTC_CALENDAR_REG_GET_FIELD(Y, rtc_calendar_reg)) + + (bcd2bin(RTC_CALENDAR_REG_GET_FIELD(C, rtc_calendar_reg)) * 100) - TM_YEAR_REF; + timeptr->tm_wday = (rtc_calendar_reg & RTC_RTC_CALENDAR_REG_RTC_DAY_Msk) - 1; + + timeptr->tm_yday = timeptr->tm_isdst = -1; /*Unknown*/ +} + +static int rtc_smartbond_set_time(const struct device *dev, const struct rtc_time *timeptr) +{ + struct rtc_smartbond_data *data = dev->data; + int ret = 0; + uint32_t rtc_time_reg, rtc_calendar_reg, rtc_status_reg; + + if (timeptr == NULL) { + LOG_ERR("No pointer is provided to set time"); + return -EINVAL; + } + + if (timeptr->tm_year + TM_YEAR_REF < TM_YEAR_REF) { + LOG_ERR("RTC time exceeds HW capabilities"); + return -EINVAL; + } + + if ((timeptr->tm_yday != -1) || (timeptr->tm_isdst != -1) || (timeptr->tm_nsec != 0)) { + LOG_WRN("Unsupported RTC sub-values"); + } + + k_mutex_lock(&data->lock, K_FOREVER); + rtc_smartbond_set_status(false); + + /* Store current counter values as it might happen that the requested time is not valid */ + rtc_time_reg = RTC->RTC_TIME_REG; + rtc_calendar_reg = RTC->RTC_CALENDAR_REG; + + RTC->RTC_TIME_REG = rtc_time_to_bcd(timeptr); + RTC->RTC_CALENDAR_REG = rtc_calendar_to_bcd(timeptr); + + /* Check if the new values were valid, otherwise reset back to the previous ones. */ + rtc_status_reg = RTC->RTC_STATUS_REG; + if (!(rtc_status_reg & RTC_RTC_STATUS_REG_RTC_VALID_CAL_Msk) || + !(rtc_status_reg & RTC_RTC_STATUS_REG_RTC_VALID_TIME_Msk)) { + RTC->RTC_TIME_REG = rtc_time_reg; + RTC->RTC_CALENDAR_REG = rtc_calendar_reg; + ret = -EINVAL; + } + + /* Mark the very first valid RTC configuration; used to check if RTC contains valid data. */ + if (!data->is_rtc_configured && (ret == 0)) { + data->is_rtc_configured = true; + } + + /* It might happen that the very first time RTC is not configured correctly; do not care. */ + rtc_smartbond_set_status(true); + k_mutex_unlock(&data->lock); + + return ret; +} + +static int rtc_smartbond_get_time(const struct device *dev, struct rtc_time *timeptr) +{ + struct rtc_smartbond_data *data = dev->data; + + if (timeptr == NULL) { + LOG_ERR("No pointer is provided to store the requested time"); + return -EINVAL; + } + + if (!data->is_rtc_configured) { + LOG_ERR("RTC is not initialized yet"); + return -ENODATA; + } + + k_mutex_lock(&data->lock, K_FOREVER); + /* Stop RTC counters to obtain coherent data. */ + rtc_smartbond_set_status(false); + + bcd_to_rtc_time(timeptr); + bcd_to_rtc_calendar(timeptr); + + rtc_smartbond_set_status(true); + k_mutex_unlock(&data->lock); + + return 0; +} + +#if defined(CONFIG_RTC_ALARM) +BUILD_ASSERT(RTC_ALARMS_COUNT, "At least one alarm event should be supported"); + +/* Define a valid calendar value as a zero sub-field is not valid for the alarm calendar value */ +static uint32_t alarm_calendar_to_bcd(const struct rtc_time *timeptr, uint16_t mask) +{ + uint32_t rtc_calendar_alarm_reg = 0x0108; + + if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { + RTC_CALENDAR_ALARM_REG_SET_FIELD(D, rtc_calendar_alarm_reg, + bin2bcd(timeptr->tm_mday)); + } + + if (mask & RTC_ALARM_TIME_MASK_MONTH) { + RTC_CALENDAR_ALARM_REG_SET_FIELD(M, rtc_calendar_alarm_reg, + bin2bcd(timeptr->tm_mon + 1)); + } + + return rtc_calendar_alarm_reg; +} + +/* No need to parse the alarm mask as a zero sub-field is valid for the alarm time counter. */ +static inline uint32_t alarm_time_to_bcd(const struct rtc_time *timeptr) +{ + uint32_t rtc_time_alarm_reg = 0; + + RTC_TIME_ALARM_REG_SET_FIELD(S, rtc_time_alarm_reg, bin2bcd(timeptr->tm_sec)); /*[0, 59]*/ + RTC_TIME_ALARM_REG_SET_FIELD(M, rtc_time_alarm_reg, bin2bcd(timeptr->tm_min)); /*[0, 59]*/ + RTC_TIME_ALARM_REG_SET_FIELD(HR, rtc_time_alarm_reg, bin2bcd(timeptr->tm_hour)); /*[0, 23]*/ + + return rtc_time_alarm_reg; +} + +static void bcd_to_alarm_calendar(struct rtc_time *timeptr) +{ + uint32_t rtc_calendar_alarm_reg = RTC->RTC_CALENDAR_ALARM_REG; + + timeptr->tm_mday = bcd2bin(RTC_CALENDAR_ALARM_REG_GET_FIELD(D, rtc_calendar_alarm_reg)); + timeptr->tm_mon = bcd2bin(RTC_CALENDAR_ALARM_REG_GET_FIELD(M, rtc_calendar_alarm_reg)) - 1; + + timeptr->tm_yday = timeptr->tm_wday = timeptr->tm_isdst = timeptr->tm_year = -1; +} + +static void bcd_to_alarm_time(struct rtc_time *timeptr) +{ + uint32_t rtc_time_alarm_reg = RTC->RTC_TIME_ALARM_REG; + + timeptr->tm_sec = bcd2bin(RTC_TIME_ALARM_REG_GET_FIELD(S, rtc_time_alarm_reg)); + timeptr->tm_min = bcd2bin(RTC_TIME_ALARM_REG_GET_FIELD(M, rtc_time_alarm_reg)); + timeptr->tm_hour = bcd2bin(RTC_TIME_ALARM_REG_GET_FIELD(HR, rtc_time_alarm_reg)); + + timeptr->tm_nsec = 0; +} + +static uint32_t tm_to_rtc_alarm_mask(uint16_t mask) +{ + uint32_t rtc_alarm_enable_reg = 0; + + if (mask & RTC_ALARM_TIME_MASK_SECOND) { + rtc_alarm_enable_reg |= RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_SEC_EN_Msk; + } + if (mask & RTC_ALARM_TIME_MASK_MINUTE) { + rtc_alarm_enable_reg |= RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_MIN_EN_Msk; + } + if (mask & RTC_ALARM_TIME_MASK_HOUR) { + rtc_alarm_enable_reg |= RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_HOUR_EN_Msk; + } + if (mask & RTC_ALARM_TIME_MASK_MONTH) { + rtc_alarm_enable_reg |= RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_MNTH_EN_Msk; + } + if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { + rtc_alarm_enable_reg |= RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_DATE_EN_Msk; + } + + return rtc_alarm_enable_reg; +} + +static uint16_t rtc_to_tm_alarm_mask(uint32_t rtc_alarm_enable_reg) +{ + uint16_t mask = 0; + + if (rtc_alarm_enable_reg & RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_SEC_EN_Msk) { + mask |= RTC_ALARM_TIME_MASK_SECOND; + } + if (rtc_alarm_enable_reg & RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_MIN_EN_Msk) { + mask |= RTC_ALARM_TIME_MASK_MINUTE; + } + if (rtc_alarm_enable_reg & RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_HOUR_EN_Msk) { + mask |= RTC_ALARM_TIME_MASK_HOUR; + } + if (rtc_alarm_enable_reg & RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_MNTH_EN_Msk) { + mask |= RTC_ALARM_TIME_MASK_MONTH; + } + if (rtc_alarm_enable_reg & RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_DATE_EN_Msk) { + mask |= RTC_ALARM_TIME_MASK_MONTHDAY; + } + + return mask; +} + +static int rtc_smartbond_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask, + const struct rtc_time *timeptr) +{ + int ret = 0; + struct rtc_smartbond_data *data = dev->data; + uint32_t rtc_time_alarm_reg; + uint32_t rtc_calendar_alarm_reg; + uint32_t rtc_alarm_enable_reg; + uint32_t rtc_status_reg; + + if (id >= RTC_ALARMS_COUNT) { + LOG_ERR("Alarm id is out of range"); + return -EINVAL; + } + + if (mask & ~RTC_SMARTBOND_SUPPORTED_ALARM_FIELDS) { + LOG_ERR("Invalid alarm mask"); + return -EINVAL; + } + + if ((timeptr == NULL) && (mask != 0)) { + LOG_ERR("No pointer is provided to set alarm"); + return -EINVAL; + } + + if (!data->is_rtc_configured) { + LOG_WRN("RTC is not initialized yet"); + } + + k_mutex_lock(&data->lock, K_FOREVER); + + rtc_alarm_enable_reg = RTC->RTC_ALARM_ENABLE_REG; + + /* Disable alarm to obtain coherency and/or when the alarm mask is empty */ + RTC->RTC_ALARM_ENABLE_REG = 0; + RTC->RTC_INTERRUPT_DISABLE_REG = RTC_RTC_INTERRUPT_DISABLE_REG_RTC_ALRM_INT_DIS_Msk; + + if (mask) { + /* Store current counter values as it might happen requested alrm is not valid */ + rtc_time_alarm_reg = RTC->RTC_TIME_ALARM_REG; + rtc_calendar_alarm_reg = RTC->RTC_CALENDAR_ALARM_REG; + + RTC->RTC_TIME_ALARM_REG = alarm_time_to_bcd(timeptr); + RTC->RTC_CALENDAR_ALARM_REG = alarm_calendar_to_bcd(timeptr, mask); + + rtc_status_reg = RTC->RTC_STATUS_REG; + if (!(rtc_status_reg & RTC_RTC_STATUS_REG_RTC_VALID_CAL_ALM_Msk) || + !(rtc_status_reg & RTC_RTC_STATUS_REG_RTC_VALID_TIME_ALM_Msk)) { + RTC->RTC_TIME_ALARM_REG = rtc_time_alarm_reg; + RTC->RTC_CALENDAR_ALARM_REG = rtc_calendar_alarm_reg; + RTC->RTC_ALARM_ENABLE_REG = rtc_alarm_enable_reg; + ret = -EINVAL; + } else { + RTC->RTC_ALARM_ENABLE_REG = tm_to_rtc_alarm_mask(mask); + } + + RTC->RTC_INTERRUPT_ENABLE_REG = RTC_RTC_INTERRUPT_ENABLE_REG_RTC_ALRM_INT_EN_Msk; + } + + k_mutex_unlock(&data->lock); + + return ret; +} + +static int rtc_smartbond_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask, + struct rtc_time *timeptr) +{ + struct rtc_smartbond_data *data = dev->data; + + if (id >= RTC_ALARMS_COUNT) { + LOG_ERR("Alarm id is out of range"); + return -EINVAL; + } + + if ((timeptr == NULL) || (mask == NULL)) { + LOG_ERR("No pointer is provided to store the requested alarm time/mask"); + return -EINVAL; + } + + if (!data->is_rtc_configured) { + LOG_WRN("RTC is not initialized yet"); + } + + k_mutex_lock(&data->lock, K_FOREVER); + + bcd_to_alarm_calendar(timeptr); + bcd_to_alarm_time(timeptr); + *mask = rtc_to_tm_alarm_mask(RTC->RTC_ALARM_ENABLE_REG); + + k_mutex_unlock(&data->lock); + + return 0; +} + +static int rtc_smartbond_alarm_is_pending(const struct device *dev, uint16_t id) +{ + unsigned int key; + int status; + struct rtc_smartbond_data *data = dev->data; + + if (id >= RTC_ALARMS_COUNT) { + LOG_ERR("Alarm id is out of range"); + return -EINVAL; + } + + /* Globally disable interrupts as the status flag can be updated within ISR */ + key = DA1469X_IRQ_DISABLE(); + status = data->is_alarm_pending; + /* After reading, the alarm status should be cleared. */ + data->is_alarm_pending = 0; + DA1469X_IRQ_ENABLE(key); + + return status; +} + +static int rtc_smartbond_alarm_set_callback(const struct device *dev, uint16_t id, + rtc_alarm_callback callback, void *user_data) +{ + struct rtc_smartbond_data *data = dev->data; + + if (id >= RTC_ALARMS_COUNT) { + LOG_ERR("Alarm id is out of range"); + return -EINVAL; + } + + k_mutex_lock(&data->lock, K_FOREVER); + + data->alarm_cb = callback; + data->alarm_user_data = user_data; + + k_mutex_unlock(&data->lock); + + return 0; +} + +static int rtc_smartbond_alarm_get_supported_fields(const struct device *dev, uint16_t id, + uint16_t *mask) +{ + if (id >= RTC_ALARMS_COUNT) { + LOG_ERR("Alarm id is out of range"); + return -EINVAL; + } + + if (mask == NULL) { + LOG_ERR("Pointer to store the mask value is missed"); + return -EINVAL; + } + + *mask = (uint16_t)RTC_SMARTBOND_SUPPORTED_ALARM_FIELDS; + + return 0; +} +#endif + +#if defined(CONFIG_RTC_UPDATE) +static int rtc_smartbond_update_set_callback(const struct device *dev, rtc_update_callback callback, + void *user_data) +{ + struct rtc_smartbond_data *data = dev->data; + + k_mutex_lock(&data->lock, K_FOREVER); + + data->update_cb = callback; + data->update_user_data = user_data; + + if (data->update_cb) { + /* Enable asserting the RTC interrupt line when the second counter rolls over. */ + RTC->RTC_INTERRUPT_ENABLE_REG = RTC_RTC_INTERRUPT_ENABLE_REG_RTC_SEC_INT_EN_Msk; + } else { + RTC->RTC_INTERRUPT_DISABLE_REG = RTC_RTC_INTERRUPT_DISABLE_REG_RTC_SEC_INT_DIS_Msk; + } + + k_mutex_unlock(&data->lock); + + return 0; +} +#endif + +struct rtc_driver_api rtc_smartbond_driver_api = { + .get_time = rtc_smartbond_get_time, + .set_time = rtc_smartbond_set_time, +#if defined(CONFIG_RTC_ALARM) + .alarm_get_time = rtc_smartbond_alarm_get_time, + .alarm_set_time = rtc_smartbond_alarm_set_time, + .alarm_is_pending = rtc_smartbond_alarm_is_pending, + .alarm_set_callback = rtc_smartbond_alarm_set_callback, + .alarm_get_supported_fields = rtc_smartbond_alarm_get_supported_fields, +#endif +#if defined(CONFIG_RTC_UPDATE) + .update_set_callback = rtc_smartbond_update_set_callback, +#endif +}; + +static void rtc_smartbond_100HZ_clock_cfg(void) +{ + const struct device * const dev = DEVICE_DT_GET(DT_NODELABEL(osc)); + uint32_t lp_clk_rate; + uint32_t clk_rtcdiv_reg; + + if (!device_is_ready(dev)) { + __ASSERT_MSG_INFO("Clock device is not ready"); + } + + if (clock_control_get_rate(dev, (clock_control_subsys_t)SMARTBOND_CLK_LP_CLK, + &lp_clk_rate) < 0) { + __ASSERT_MSG_INFO("Cannot extract LP clock rate"); + } + + clk_rtcdiv_reg = CRG_TOP->CLK_RTCDIV_REG; + CLK_RTCDIV_REG_SET_FIELD(DENOM, clk_rtcdiv_reg, RTC_DIV_DENOM_1000); + CLK_RTCDIV_REG_SET_FIELD(INT, clk_rtcdiv_reg, lp_clk_rate / 100); + CLK_RTCDIV_REG_SET_FIELD(FRAC, clk_rtcdiv_reg, (lp_clk_rate % 100) * 10); + CRG_TOP->CLK_RTCDIV_REG = clk_rtcdiv_reg; +} + +static int rtc_smartbond_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* Wakeup device from RTC events (alarm/roll over) */ +#if CONFIG_PM + bool is_xtal32m_enabled = DT_NODE_HAS_STATUS(DT_NODELABEL(xtal32m), okay); + int pdc_idx = da1469x_pdc_add(MCU_PDC_TRIGGER_RTC_ALARM, MCU_PDC_MASTER_M33, + is_xtal32m_enabled ? MCU_PDC_EN_XTAL : 0); + + __ASSERT(pdc_idx >= 0, "Failed to add RTC PDC entry"); + da1469x_pdc_set(pdc_idx); + da1469x_pdc_ack(pdc_idx); +#endif + + rtc_smartbond_100HZ_clock_cfg(); + + /* Timer and calendar counters will not reset after SW reset */ + RTC->RTC_KEEP_RTC_REG |= RTC_RTC_KEEP_RTC_REG_RTC_KEEP_Msk; + +#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) + IRQ_CONNECT(SMARTBOND_IRQN, SMARTBOND_IRQ_PRIO, smartbond_rtc_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(SMARTBOND_IRQN); +#endif + + return 0; +} + +#define SMARTBOND_RTC_INIT(inst) \ + BUILD_ASSERT((inst) == 0, "multiple instances are not supported"); \ + \ + static struct rtc_smartbond_data rtc_smartbond_data_ ## inst; \ + \ + DEVICE_DT_INST_DEFINE(0, rtc_smartbond_init, NULL, \ + &rtc_smartbond_data_ ## inst, NULL, \ + POST_KERNEL, \ + CONFIG_RTC_INIT_PRIORITY, \ + &rtc_smartbond_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(SMARTBOND_RTC_INIT) diff --git a/dts/bindings/rtc/renesas,smartbond-rtc.yaml b/dts/bindings/rtc/renesas,smartbond-rtc.yaml new file mode 100644 index 00000000000..afe21aef79b --- /dev/null +++ b/dts/bindings/rtc/renesas,smartbond-rtc.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas SmartBond(tm) RTC node + +compatible: "renesas,smartbond-rtc" + +include: rtc-device.yaml + +properties: + reg: + required: true + + interrupts: + required: true