diff --git a/drivers/rtc/CMakeLists.txt b/drivers/rtc/CMakeLists.txt index 18760af70a0f7..fe8e2fc631cf0 100644 --- a/drivers/rtc/CMakeLists.txt +++ b/drivers/rtc/CMakeLists.txt @@ -36,6 +36,7 @@ zephyr_library_sources_ifdef(CONFIG_RTC_RV3032 rtc_rv3032.c) zephyr_library_sources_ifdef(CONFIG_RTC_RV8263 rtc_rv8263.c) zephyr_library_sources_ifdef(CONFIG_RTC_RV8803 rtc_rv8803.c) zephyr_library_sources_ifdef(CONFIG_RTC_RX8130CE rtc_rx8130ce.c) +zephyr_library_sources_ifdef(CONFIG_RTC_SF32LB rtc_sf32lb.c) zephyr_library_sources_ifdef(CONFIG_RTC_SILABS_SIWX91X rtc_silabs_siwx91x.c) zephyr_library_sources_ifdef(CONFIG_RTC_SMARTBOND rtc_smartbond.c) zephyr_library_sources_ifdef(CONFIG_RTC_STM32 rtc_ll_stm32.c) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 5d8bd13ccfacb..61e98d0c3c46a 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -68,6 +68,7 @@ source "drivers/rtc/Kconfig.rv8803" source "drivers/rtc/Kconfig.rx8130ce" source "drivers/rtc/Kconfig.sam" source "drivers/rtc/Kconfig.sam0" +source "drivers/rtc/Kconfig.sf32lb" source "drivers/rtc/Kconfig.siwx91x" source "drivers/rtc/Kconfig.smartbond" source "drivers/rtc/Kconfig.stm32" diff --git a/drivers/rtc/Kconfig.sf32lb b/drivers/rtc/Kconfig.sf32lb new file mode 100644 index 0000000000000..22e74b96a28e0 --- /dev/null +++ b/drivers/rtc/Kconfig.sf32lb @@ -0,0 +1,9 @@ +# Copyright (c) 2025 Qingsong Gou +# SPDX-License-Identifier: Apache-2.0 + +config RTC_SF32LB + bool "RTC driver for SF32LB family of MCUs" + default y + depends on DT_HAS_SIFLI_SF32LB_RTC_ENABLED + help + Enable RTC driver for SF32LB series of MCUs diff --git a/drivers/rtc/rtc_sf32lb.c b/drivers/rtc/rtc_sf32lb.c new file mode 100644 index 0000000000000..b68f7cfda68d1 --- /dev/null +++ b/drivers/rtc/rtc_sf32lb.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2025 Qingsong Gou + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT sifli_sf32lb_rtc + +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(rtc_sf32lb, CONFIG_RTC_LOG_LEVEL); + +#define RTC_TR offsetof(RTC_TypeDef, TR) +#define RTC_DR offsetof(RTC_TypeDef, DR) +#define RTC_CR offsetof(RTC_TypeDef, CR) +#define RTC_ISR offsetof(RTC_TypeDef, ISR) +#define RTC_PSCLR offsetof(RTC_TypeDef, PSCLR) +#define RTC_WUTR offsetof(RTC_TypeDef, WUTR) +#define RTC_ALRMTR offsetof(RTC_TypeDef, ALRMTR) +#define RTC_ALRMDR offsetof(RTC_TypeDef, ALRMDR) + +struct rtc_sf32lb_config { + uintptr_t base; +}; + +static int rtc_sf32lb_enter_init_mode(const struct device *dev) +{ + const struct rtc_sf32lb_config *config = dev->config; + + sys_set_bit(config->base + RTC_ISR, RTC_ISR_INIT_Pos); + + while (!sys_test_bit(config->base + RTC_ISR, RTC_ISR_INITF_Pos)) { + } + + return 0; +} + +static int rtc_sf32lb_exit_init_mode(const struct device *dev) +{ + const struct rtc_sf32lb_config *config = dev->config; + + sys_clear_bit(config->base + RTC_ISR, RTC_ISR_INIT_Pos); + + return 0; +} + +static void rtc_sf32lb_wait_for_sync(const struct device *dev) +{ + const struct rtc_sf32lb_config *config = dev->config; + + sys_clear_bit(config->base + RTC_ISR, RTC_ISR_RSF_Pos); + + while (!sys_test_bit(config->base + RTC_ISR, RTC_ISR_RSF_Pos)) { + } +} + +static int rtc_sf32lb_set_time(const struct device *dev, const struct rtc_time *timeptr) +{ + const struct rtc_sf32lb_config *config = dev->config; + uint32_t tr = 0; + uint32_t dr = 0; + + __ASSERT_NO_MSG(timeptr != NULL); + + tr = (uint32_t)(((uint32_t)bin2bcd(timeptr->tm_hour) << RTC_TR_HU_Pos) | + ((uint32_t)bin2bcd(timeptr->tm_min) << RTC_TR_MNU_Pos) | + ((uint32_t)bin2bcd(timeptr->tm_sec) << RTC_TR_SU_Pos)); + + rtc_sf32lb_enter_init_mode(dev); + sys_write32(tr, config->base + RTC_TR); + rtc_sf32lb_exit_init_mode(dev); + + if (timeptr->tm_year < 100) { + dr |= RTC_DR_CB; + dr |= (((uint32_t)bin2bcd(timeptr->tm_year) << RTC_DR_YU_Pos) | + ((uint32_t)bin2bcd(timeptr->tm_mon + 1) << RTC_DR_MU_Pos) | + ((uint32_t)bin2bcd(timeptr->tm_mday)) | + ((uint32_t)timeptr->tm_wday << RTC_DR_WD_Pos)); + } else { + dr |= (((uint32_t)bin2bcd(timeptr->tm_year - 100) << RTC_DR_YU_Pos) | + ((uint32_t)bin2bcd(timeptr->tm_mon + 1) << RTC_DR_MU_Pos) | + ((uint32_t)bin2bcd(timeptr->tm_mday)) | + ((uint32_t)timeptr->tm_wday << RTC_DR_WD_Pos)); + } + + rtc_sf32lb_enter_init_mode(dev); + sys_write32(dr, config->base + RTC_DR); + rtc_sf32lb_exit_init_mode(dev); + + if (!sys_test_bit(config->base + RTC_CR, RTC_CR_BYPSHAD_Pos)) { + rtc_sf32lb_wait_for_sync(dev); + } + + return 0; +} + +static int rtc_sf32lb_get_time(const struct device *dev, struct rtc_time *timeptr) +{ + const struct rtc_sf32lb_config *config = dev->config; + uint32_t reg; + + __ASSERT_NO_MSG(timeptr != NULL); + + reg = sys_read32(config->base + RTC_TR); + + timeptr->tm_hour = bcd2bin((uint8_t)((reg & (RTC_TR_HT | RTC_TR_HU)) >> RTC_TR_HU_Pos)); + timeptr->tm_min = bcd2bin((uint8_t)((reg & (RTC_TR_MNT | RTC_TR_MNU)) >> RTC_TR_MNU_Pos)); + timeptr->tm_sec = bcd2bin((uint8_t)((reg & (RTC_TR_ST | RTC_TR_SU)) >> RTC_TR_SU_Pos)); + + reg = sys_read32(config->base + RTC_DR); + if (reg & RTC_DR_CB) { + timeptr->tm_year = + bcd2bin((uint8_t)((reg & (RTC_DR_YT | RTC_DR_YU)) >> RTC_DR_YU_Pos)); + } else { + timeptr->tm_year = + bcd2bin((uint8_t)((reg & (RTC_DR_YT | RTC_DR_YU)) >> RTC_DR_YU_Pos)) + 100; + } + timeptr->tm_mon = bcd2bin((uint8_t)((reg & (RTC_DR_MT | RTC_DR_MU)) >> RTC_DR_MU_Pos)) - 1; + timeptr->tm_mday = bcd2bin((uint8_t)(reg & (RTC_DR_DT | RTC_DR_DU))); + timeptr->tm_wday = bcd2bin((uint8_t)((reg & (RTC_DR_WD)) >> RTC_DR_WD_Pos)); + + return 0; +} + +static int rtc_sf32lb_init(const struct device *dev) +{ + const struct rtc_sf32lb_config *config = dev->config; + uint32_t psclr = 0; + + psclr |= (38U << RTC_PSCLR_DIVA_INT_Pos) & RTC_PSCLR_DIVA_INT_Msk; + psclr |= (4608U << RTC_PSCLR_DIVA_FRAC_Pos) & RTC_PSCLR_DIVA_FRAC_Msk; + psclr |= (256U << RTC_PSCLR_DIVB_Pos) & RTC_PSCLR_DIVB_Msk; + sys_write32(psclr, config->base + RTC_PSCLR); + + if (!sys_test_bit(config->base + RTC_CR, RTC_CR_BYPSHAD_Pos)) { + rtc_sf32lb_wait_for_sync(dev); + } + + return 0; +} + +static const struct rtc_driver_api rtc_sf32lb_driver_api = { + .set_time = rtc_sf32lb_set_time, + .get_time = rtc_sf32lb_get_time, +}; + +#define RTC_SF32LB_INIT(n) \ + static const struct rtc_sf32lb_config rtc_sf32lb_config_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + }; \ + DEVICE_DT_INST_DEFINE(n, rtc_sf32lb_init, NULL, NULL, &rtc_sf32lb_config_##n, POST_KERNEL, \ + CONFIG_RTC_INIT_PRIORITY, &rtc_sf32lb_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(RTC_SF32LB_INIT)