/* * Copyright (c) 2018 Workaround GmbH * Copyright (c) 2018 Allterco Robotics * Copyright (c) 2018 Linaro Limited * * SPDX-License-Identifier: Apache-2.0 * * Source file for the STM32 RTC driver * */ #define DT_DRV_COMPAT st_stm32_rtc #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(counter_rtc_stm32, CONFIG_COUNTER_LOG_LEVEL); #if CONFIG_STM32_HAL2 #define STM32_RTC_HOUR_FORMAT_24HOUR LL_RTC_HOUR_FORMAT_24HOUR #define STM32_RTC_HOUR_FORMAT_AMPM LL_RTC_HOUR_FORMAT_AMPM #define STM32_RTC_TIME_FORMAT_AM_24H LL_RTC_TIME_FORMAT_AM_24H #define STM32_RTC_GET_SECOND LL_RTC_GET_SECOND #define STM32_RTC_GET_MINUTE LL_RTC_GET_MINUTE #define STM32_RTC_GET_HOUR LL_RTC_GET_HOUR #define STM32_RTC_GET_DAY LL_RTC_GET_DAY #define STM32_RTC_GET_WEEKDAY LL_RTC_GET_WEEKDAY #define STM32_RTC_GET_YEAR LL_RTC_GET_YEAR #define STM32_RTC_GET_MONTH LL_RTC_GET_MONTH #define STM32_RTC_EnableBypassShadowReg LL_RTC_EnableBypassShadowReg #define STM32_RTC_DisableBypassShadowReg LL_RTC_DisableBypassShadowReg /* On HAL2 this macro skips the RTC instance as the first argument, * only the potential following arguments are passed. */ #define STM32_ARG(dev, ...) __VA_ARGS__ #else /* CONFIG_STM32_HAL2 */ #define STM32_RTC_HOUR_FORMAT_24HOUR LL_RTC_HOURFORMAT_24HOUR #define STM32_RTC_HOUR_FORMAT_AMPM LL_RTC_HOURFORMAT_AMPM #define STM32_RTC_TIME_FORMAT_AM_24H LL_RTC_TIME_FORMAT_AM_OR_24 #define STM32_RTC_GET_SECOND __LL_RTC_GET_SECOND #define STM32_RTC_GET_MINUTE __LL_RTC_GET_MINUTE #define STM32_RTC_GET_HOUR __LL_RTC_GET_HOUR #define STM32_RTC_GET_DAY __LL_RTC_GET_DAY #define STM32_RTC_GET_WEEKDAY __LL_RTC_GET_WEEKDAY #define STM32_RTC_GET_MONTH __LL_RTC_GET_MONTH #define STM32_RTC_GET_YEAR __LL_RTC_GET_YEAR #define STM32_RTC_EnableBypassShadowReg LL_RTC_EnableShadowRegBypass #define STM32_RTC_DisableBypassShadowReg LL_RTC_DisableShadowRegBypass /* On HAL1 this macro adds the RTC instance as the first argument, * with or without a comma depending on number of arguments. */ #define STM32_ARG(dev, ...) COND_CODE_1(IS_EMPTY(__VA_ARGS__), (dev), (dev, __VA_ARGS__)) #endif /* CONFIG_STM32_HAL2 */ #if defined(CONFIG_SOC_SERIES_STM32F1X) || defined(CONFIG_SOC_SERIES_STM32F2X) || \ (defined(CONFIG_SOC_SERIES_STM32L1X) && !defined(RTC_SUBSECOND_SUPPORT)) /* subsecond counting is not supported by some STM32L1x MCUs (Cat.1) & by STM32F1x/2x SoC series */ #define HW_SUBSECOND_SUPPORT 0 #else #define HW_SUBSECOND_SUPPORT 1 #endif /* Seconds from 1970-01-01T00:00:00 to 2000-01-01T00:00:00 */ #define T_TIME_OFFSET 946684800 #if DT_INST_NODE_HAS_PROP(0, alrm_exti_line) #define RTC_EXTI_LINE_NUM DT_INST_PROP(0, alrm_exti_line) #endif /* DT_INST_NODE_HAS_PROP(0, alrm_exti_line) */ #if defined(CONFIG_SOC_SERIES_STM32F1X) #define COUNTER_NO_DATE #endif #if DT_INST_CLOCKS_CELL_BY_IDX(0, 1, bus) == STM32_SRC_LSI /* LSI */ #define RTCCLK_FREQ STM32_LSI_FREQ #else /* LSE */ #define RTCCLK_FREQ STM32_LSE_FREQ #endif /* DT_INST_CLOCKS_CELL_BY_IDX(0, 1, bus) == STM32_SRC_LSI */ #if !defined(CONFIG_SOC_SERIES_STM32F1X) #ifndef CONFIG_COUNTER_RTC_STM32_SUBSECONDS #define RTC_ASYNCPRE BIT_MASK(7) #else /* !CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ /* Get the highest possible clock for the subsecond register */ #define RTC_ASYNCPRE 1 #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ #else /* CONFIG_SOC_SERIES_STM32F1X */ #define RTC_ASYNCPRE (RTCCLK_FREQ - 1) #endif /* CONFIG_SOC_SERIES_STM32F1X */ /* Timeout in microseconds used to wait for flags */ #define RTC_TIMEOUT 1000 /* Adjust the second sync prescaler to get 1Hz on ck_spre */ #define RTC_SYNCPRE ((RTCCLK_FREQ / (1 + RTC_ASYNCPRE)) - 1) #ifndef CONFIG_COUNTER_RTC_STM32_SUBSECONDS typedef uint32_t tick_t; #else typedef uint64_t tick_t; #endif struct rtc_stm32_config { struct counter_config_info counter_info; uint32_t async_prescaler; #if !defined(CONFIG_SOC_SERIES_STM32F1X) uint32_t sync_prescaler; #endif /* !CONFIG_SOC_SERIES_STM32F1X */ const struct stm32_pclken *pclken; #if DT_INST_CLOCKS_CELL_BY_IDX(0, 1, bus) == STM32_SRC_HSE uint32_t hse_prescaler; #endif }; struct rtc_stm32_data { counter_alarm_callback_t callback; uint32_t ticks; void *user_data; #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS bool irq_on_late; #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ }; static inline void ll_clear_alarm_flag(void) { #if defined(CONFIG_SOC_SERIES_STM32F1X) LL_RTC_ClearFlag_ALR(STM32_ARG(RTC)); #else LL_RTC_ClearFlag_ALRA(STM32_ARG(RTC)); #endif } static inline uint32_t ll_is_active_alarm(void) { #if defined(CONFIG_SOC_SERIES_STM32F1X) return LL_RTC_IsActiveFlag_ALR(STM32_ARG(RTC)); #else return LL_RTC_IsActiveFlag_ALRA(STM32_ARG(RTC)); #endif } static inline void ll_enable_interrupt_alarm(void) { #if defined(CONFIG_SOC_SERIES_STM32F1X) LL_RTC_EnableIT_ALR(STM32_ARG(RTC)); #else LL_RTC_EnableIT_ALRA(STM32_ARG(RTC)); #endif } static inline void ll_disable_interrupt_alarm(void) { #if defined(CONFIG_SOC_SERIES_STM32F1X) LL_RTC_DisableIT_ALR(STM32_ARG(RTC)); #else LL_RTC_DisableIT_ALRA(STM32_ARG(RTC)); #endif } #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS static inline uint32_t ll_isenabled_interrupt_alarm(void) { #if defined(CONFIG_SOC_SERIES_STM32F1X) return LL_RTC_IsEnabledIT_ALR(STM32_ARG(RTC)); #else return LL_RTC_IsEnabledIT_ALRA(STM32_ARG(RTC)); #endif } #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ static inline void ll_enable_alarm(void) { #if !defined(CONFIG_SOC_SERIES_STM32F1X) LL_RTC_ALMA_Enable(STM32_ARG(RTC)); #endif } static inline void ll_disable_alarm(void) { #if !defined(CONFIG_SOC_SERIES_STM32F1X) LL_RTC_ALMA_Disable(STM32_ARG(RTC)); #endif } static void rtc_stm32_irq_config(const struct device *dev); /* When no error occurs, this function disables the RTC write protection and should be balanced * with a call to rtc_stm32_exit_init_mode (which enables RTC write protection). * In case of error, the write protection is enabled when leaving this function, so nothing more * needs to be made. */ static int rtc_stm32_enter_init_mode(void) { #if defined(CONFIG_SOC_SERIES_STM32F1X) /* Wait for RTC to be ready */ if (!WAIT_FOR(LL_RTC_IsActiveFlag_RTOF(STM32_ARG(RTC)), RTC_TIMEOUT, NULL)) { return -ETIMEDOUT; } LL_RTC_DisableWriteProtection(STM32_ARG(RTC)); #else LL_RTC_DisableWriteProtection(STM32_ARG(RTC)); /* Check if the Initialization mode is set */ if (LL_RTC_IsActiveFlag_INIT(STM32_ARG(RTC)) == 0U) { /* Set the Initialization mode */ LL_RTC_EnableInitMode(STM32_ARG(RTC)); if (!WAIT_FOR(LL_RTC_IsActiveFlag_INIT(STM32_ARG(RTC)), RTC_TIMEOUT, NULL)) { LL_RTC_DisableInitMode(STM32_ARG(RTC)); LL_RTC_EnableWriteProtection(STM32_ARG(RTC)); return -ETIMEDOUT; } } #endif return 0; } static int rtc_stm32_exit_init_mode(void) { int status = 0; #if defined(CONFIG_SOC_SERIES_STM32F1X) LL_RTC_EnableWriteProtection(STM32_ARG(RTC)); /* Wait for RTC to be ready */ if (!WAIT_FOR(LL_RTC_IsActiveFlag_RTOF(STM32_ARG(RTC)), RTC_TIMEOUT, NULL)) { status = -ETIMEDOUT; } #else LL_RTC_DisableInitMode(STM32_ARG(RTC)); LL_RTC_EnableWriteProtection(STM32_ARG(RTC)); #endif return status; } #if !defined(CONFIG_COUNTER_RTC_STM32_SAVE_VALUE_BETWEEN_RESETS) static int rtc_stm32_wait_for_synchro(void) { int status = 0; /* Clear RSF flag */ LL_RTC_ClearFlag_RS(STM32_ARG(RTC)); if (!WAIT_FOR(LL_RTC_IsActiveFlag_RS(STM32_ARG(RTC)), RTC_TIMEOUT, NULL)) { status = -ETIMEDOUT; } return status; } static int rtc_stm32_deinit(void) { int ret; /* Set Initialization mode */ ret = rtc_stm32_enter_init_mode(); if (ret < 0) { LOG_ERR("Failed to enter RTC init mode"); return ret; } #if defined(CONFIG_SOC_SERIES_STM32F1X) stm32_reg_write(&RTC->CNTL, 0U); stm32_reg_write(&RTC->CNTH, 0U); stm32_reg_write(&RTC->PRLH, 0U); stm32_reg_write(&RTC->PRLL, 0x8000U); stm32_reg_write(&RTC->CRH, 0U); stm32_reg_write(&RTC->CRL, 0x20U); #else /* CONFIG_SOC_SERIES_STM32F1X */ stm32_reg_write(&RTC->CR, 0U); stm32_reg_write(&RTC->TR, 0U); #ifdef RTC_WUTR_WUT stm32_reg_write(&RTC->WUTR, RTC_WUTR_WUT); #endif /* RTC_WUTR_WUT */ stm32_reg_write(&RTC->DR, RTC_DR_WDU_0 | RTC_DR_MU_0 | RTC_DR_DU_0); stm32_reg_write(&RTC->PRER, RTC_PRER_PREDIV_A | 0xFFU); stm32_reg_write(&RTC->ALRMAR, 0U); #ifdef RTC_CR_ALRBE stm32_reg_write(&RTC->ALRMBR, 0U); #endif /* RTC_CR_ALRBE */ #if HW_SUBSECOND_SUPPORT stm32_reg_write(&RTC->CALR, 0U); stm32_reg_write(&RTC->SHIFTR, 0U); stm32_reg_write(&RTC->ALRMASSR, 0U); #ifdef RTC_CR_ALRBE stm32_reg_write(&RTC->ALRMBSSR, 0U); #endif /* RTC_CR_ALRBE */ #endif /* HW_SUBSECOND_SUPPORT */ #if defined(RTC_PRIVCFGR_PRIV) stm32_reg_write(&RTC->PRIVCFGR, 0U); #endif /* RTC_PRIVCFGR_PRIV */ #if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) stm32_reg_write(&RTC->SECCFGR, 0U); #endif /* (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ /* Reset I(C)SR register and exit initialization mode */ #ifdef RTC_ICSR_INIT stm32_reg_write(&RTC->ICSR, 0U); #else stm32_reg_write(&RTC->ISR, 0U); #endif #endif /* CONFIG_SOC_SERIES_STM32F1X */ /* Exit Initialization mode */ ret = rtc_stm32_exit_init_mode(); if (ret < 0) { LOG_ERR("Failed to exit RTC init mode"); return ret; } return rtc_stm32_wait_for_synchro(); } #endif static int rtc_stm32_configure(const struct device *dev) { const struct rtc_stm32_config *cfg = dev->config; int ret; /* Set Initialization mode */ ret = rtc_stm32_enter_init_mode(); if (ret < 0) { LOG_ERR("Failed to enter RTC init mode"); return ret; } #if defined(CONFIG_SOC_SERIES_STM32F1X) LL_RTC_SetAsynchPrescaler(STM32_ARG(RTC, cfg->async_prescaler)); LL_RTC_SetOutputSource(BKP, LL_RTC_CALIB_OUTPUT_NONE); #else LL_RTC_SetHourFormat(STM32_ARG(RTC, STM32_RTC_HOUR_FORMAT_24HOUR)); LL_RTC_SetAsynchPrescaler(STM32_ARG(RTC, cfg->async_prescaler)); LL_RTC_SetSynchPrescaler(STM32_ARG(RTC, cfg->sync_prescaler)); #endif /* Exit Initialization mode */ ret = rtc_stm32_exit_init_mode(); if (ret < 0) { LOG_ERR("Failed to exit RTC init mode"); } return ret; } static int rtc_stm32_start(const struct device *dev) { #if defined(CONFIG_SOC_SERIES_STM32WBAX) || defined(CONFIG_SOC_SERIES_STM32U5X) const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); const struct rtc_stm32_config *cfg = dev->config; /* Enable RTC bus clock */ if (clock_control_on(clk, (clock_control_subsys_t) &cfg->pclken[0]) != 0) { LOG_ERR("RTC clock enabling failed"); return -EIO; } #else ARG_UNUSED(dev); z_stm32_hsem_lock(CFG_HW_RCC_SEMID, HSEM_LOCK_DEFAULT_RETRY); stm32_backup_domain_enable_access(); #ifdef CONFIG_SOC_SERIES_STM32U3X /* STM32U3 series uses LL_RCC_RTC_ClockEnable instead of LL_RCC_EnableRTC */ LL_RCC_RTC_ClockEnable(); #else LL_RCC_EnableRTC(); #endif /* CONFIG_SOC_SERIES_STM32U3X */ stm32_backup_domain_disable_access(); z_stm32_hsem_unlock(CFG_HW_RCC_SEMID); #endif /* CONFIG_SOC_SERIES_STM32WBAX || CONFIG_SOC_SERIES_STM32U5X */ return 0; } static int rtc_stm32_stop(const struct device *dev) { #if defined(CONFIG_SOC_SERIES_STM32WBAX) || defined(CONFIG_SOC_SERIES_STM32U5X) const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); const struct rtc_stm32_config *cfg = dev->config; /* Disable RTC bus clock */ if (clock_control_off(clk, (clock_control_subsys_t) &cfg->pclken[0]) != 0) { LOG_ERR("RTC clock disabling failed"); return -EIO; } #else ARG_UNUSED(dev); z_stm32_hsem_lock(CFG_HW_RCC_SEMID, HSEM_LOCK_DEFAULT_RETRY); stm32_backup_domain_enable_access(); #ifdef CONFIG_SOC_SERIES_STM32U3X /* STM32U3 series uses LL_RCC_RTC_ClockDisable instead of LL_RCC_DisableRTC */ LL_RCC_RTC_ClockDisable(); #else LL_RCC_DisableRTC(); #endif /* CONFIG_SOC_SERIES_STM32U3X */ stm32_backup_domain_disable_access(); z_stm32_hsem_unlock(CFG_HW_RCC_SEMID); #endif /* CONFIG_SOC_SERIES_STM32WBAX || CONFIG_SOC_SERIES_STM32U5X */ return 0; } #if !defined(COUNTER_NO_DATE) tick_t rtc_stm32_read(const struct device *dev) { struct tm now = { 0 }; time_t ts; uint32_t rtc_date, rtc_time; tick_t ticks; #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS uint32_t rtc_subsecond; #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ ARG_UNUSED(dev); do { /* read date, time and subseconds and relaunch if a day increment occurred * while doing so as it will result in an erroneous result otherwise */ rtc_date = LL_RTC_DATE_Get(STM32_ARG(RTC)); do { /* read time and subseconds and relaunch if a second increment occurred * while doing so as it will result in an erroneous result otherwise */ rtc_time = LL_RTC_TIME_Get(STM32_ARG(RTC)); #if CONFIG_COUNTER_RTC_STM32_SUBSECONDS do { /* read subseconds and relaunch if a second increment occurred * while doing so as it will result in an erroneous result otherwise */ rtc_subsecond = LL_RTC_TIME_GetSubSecond(STM32_ARG(RTC)); } while (rtc_subsecond != LL_RTC_TIME_GetSubSecond(STM32_ARG(RTC))); #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ } while (rtc_time != LL_RTC_TIME_Get(STM32_ARG(RTC))); } while (rtc_date != LL_RTC_DATE_Get(STM32_ARG(RTC))); /* Convert calendar datetime to UNIX timestamp */ /* RTC start time: 1st, Jan, 2000 */ /* time_t start: 1st, Jan, 1970 */ now.tm_year = 100 + bcd2bin(STM32_RTC_GET_YEAR(rtc_date)); /* tm_mon allowed values are 0-11 */ now.tm_mon = bcd2bin(STM32_RTC_GET_MONTH(rtc_date)) - 1; now.tm_mday = bcd2bin(STM32_RTC_GET_DAY(rtc_date)); now.tm_hour = bcd2bin(STM32_RTC_GET_HOUR(rtc_time)); now.tm_min = bcd2bin(STM32_RTC_GET_MINUTE(rtc_time)); now.tm_sec = bcd2bin(STM32_RTC_GET_SECOND(rtc_time)); ts = timeutil_timegm(&now); /* Return number of seconds since RTC init */ ts -= T_TIME_OFFSET; __ASSERT(sizeof(time_t) == 8, "unexpected time_t definition"); ticks = ts * counter_get_frequency(dev); #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS /* The RTC counts up, except for the subsecond register which counts * down starting from the sync prescaler value. Add already counted * ticks. */ ticks += RTC_SYNCPRE - rtc_subsecond; #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ return ticks; } #else /* defined(COUNTER_NO_DATE) */ tick_t rtc_stm32_read(const struct device *dev) { uint32_t ticks; ARG_UNUSED(dev); ticks = LL_RTC_TIME_Get(STM32_ARG(RTC)); return ticks; } #endif /* !defined(COUNTER_NO_DATE) */ static int rtc_stm32_get_value(const struct device *dev, uint32_t *ticks) { *ticks = (uint32_t)rtc_stm32_read(dev); return 0; } #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS static int rtc_stm32_get_value_64(const struct device *dev, uint64_t *ticks) { *ticks = rtc_stm32_read(dev); return 0; } #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS static void rtc_stm32_set_int_pending(void) { k_irq_set_pending(DT_INST_IRQN(0)); } #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ static int rtc_stm32_set_alarm(const struct device *dev, uint8_t chan_id, const struct counter_alarm_cfg *alarm_cfg) { #if !defined(COUNTER_NO_DATE) struct tm alarm_tm; time_t alarm_val_s; #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS uint32_t alarm_val_ss; #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ #else uint32_t remain; #endif struct rtc_stm32_data *data = dev->data; int ret = 0; tick_t now = rtc_stm32_read(dev); tick_t ticks = alarm_cfg->ticks; if (data->callback != NULL) { LOG_DBG("Alarm busy"); return -EBUSY; } data->callback = alarm_cfg->callback; data->user_data = alarm_cfg->user_data; #if !defined(COUNTER_NO_DATE) if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) { /* Add +1 in order to compensate the partially started tick. * Alarm will expire between requested ticks and ticks+1. * In case only 1 tick is requested, it will avoid * that tick+1 event occurs before alarm setting is finished. */ ticks += now + 1; alarm_val_s = (time_t)(ticks / counter_get_frequency(dev)) + T_TIME_OFFSET; } else { alarm_val_s = (time_t)(ticks / counter_get_frequency(dev)); } gmtime_r(&alarm_val_s, &alarm_tm); #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS alarm_val_ss = ticks % counter_get_frequency(dev); LOG_DBG("Set Alarm: %llu", ticks); #else /* !CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ LOG_DBG("Set Alarm: %d", ticks); #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ #else if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) { remain = ticks + now + 1; } else { remain = ticks; } /* In F1X, an interrupt occurs when the counter expires, * not when the counter matches, so set -1 */ remain--; #endif stm32_backup_domain_enable_access(); #if !defined(COUNTER_NO_DATE) LL_RTC_DisableWriteProtection(STM32_ARG(RTC)); ll_disable_alarm(); /* Configure the Alarm registers */ LL_RTC_ALMA_DisableWeekday(STM32_ARG(RTC)); LL_RTC_ALMA_SetDay(STM32_ARG(RTC, bin2bcd(alarm_tm.tm_mday))); LL_RTC_ALMA_ConfigTime(STM32_ARG(RTC, STM32_RTC_TIME_FORMAT_AM_24H, bin2bcd(alarm_tm.tm_hour), bin2bcd(alarm_tm.tm_min), bin2bcd(alarm_tm.tm_sec))); LL_RTC_ALMA_SetMask(STM32_ARG(RTC, LL_RTC_ALMA_MASK_NONE)); LL_RTC_EnableWriteProtection(STM32_ARG(RTC)); #else /* Set Initialization mode */ ret = rtc_stm32_enter_init_mode(); if (ret < 0) { goto out_disable_bkup_access; } /* Set the alarm */ LL_RTC_ALARM_Set(RTC, remain); ret = rtc_stm32_exit_init_mode(); if (ret < 0) { goto out_disable_bkup_access; } #endif LL_RTC_DisableWriteProtection(STM32_ARG(RTC)); #if HW_SUBSECOND_SUPPORT #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS /* Care about all bits of the subsecond register */ LL_RTC_ALMA_SetSubSecondMask(STM32_ARG(RTC, 0xF)); LL_RTC_ALMA_SetSubSecond(STM32_ARG(RTC, RTC_SYNCPRE - alarm_val_ss)); #else LL_RTC_ALMA_SetSubSecondMask(STM32_ARG(RTC, 0)); #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ #endif /* HW_SUBSECOND_SUPPORT */ ll_enable_alarm(); ll_clear_alarm_flag(); ll_enable_interrupt_alarm(); LL_RTC_EnableWriteProtection(STM32_ARG(RTC)); #if defined(COUNTER_NO_DATE) out_disable_bkup_access: #endif stm32_backup_domain_disable_access(); #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS /* The reference manual says: * "Each change of the RTC_CR register is taken into account after * 1 to 2 RTCCLK clock cycles due to clock synchronization." * It means we need at least two cycles after programming the CR * register. It is confirmed experimentally. * * It should happen only if one tick alarm is requested and a tick * occurs while processing the function. Trigger the irq manually in * this case. */ now = rtc_stm32_read(dev); if ((ticks - now < 2) || (now > ticks)) { data->irq_on_late = true; rtc_stm32_set_int_pending(); } #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ return ret; } static int rtc_stm32_cancel_alarm(const struct device *dev, uint8_t chan_id) { struct rtc_stm32_data *data = dev->data; stm32_backup_domain_enable_access(); LL_RTC_DisableWriteProtection(STM32_ARG(RTC)); ll_clear_alarm_flag(); ll_disable_interrupt_alarm(); ll_disable_alarm(); LL_RTC_EnableWriteProtection(STM32_ARG(RTC)); stm32_backup_domain_disable_access(); data->callback = NULL; return 0; } static uint32_t rtc_stm32_get_pending_int(const struct device *dev) { return ll_is_active_alarm() != 0; } static uint32_t rtc_stm32_get_top_value(const struct device *dev) { const struct counter_config_info *info = dev->config; return info->max_top_value; } static int rtc_stm32_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg) { const struct counter_config_info *info = dev->config; if ((cfg->ticks != info->max_top_value) || !(cfg->flags & COUNTER_TOP_CFG_DONT_RESET)) { return -ENOTSUP; } else { return 0; } } void rtc_stm32_isr(const struct device *dev) { struct rtc_stm32_data *data = dev->data; counter_alarm_callback_t alarm_callback = data->callback; uint32_t now = rtc_stm32_read(dev); if (ll_is_active_alarm() != 0 #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS || (data->irq_on_late && ll_isenabled_interrupt_alarm()) #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ ) { stm32_backup_domain_enable_access(); LL_RTC_DisableWriteProtection(STM32_ARG(RTC)); ll_clear_alarm_flag(); ll_disable_interrupt_alarm(); ll_disable_alarm(); LL_RTC_EnableWriteProtection(STM32_ARG(RTC)); stm32_backup_domain_disable_access(); #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS data->irq_on_late = false; #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ if (alarm_callback != NULL) { data->callback = NULL; alarm_callback(dev, 0, now, data->user_data); } } #if defined(RTC_EXTI_LINE_NUM) stm32_exti_clear_pending(RTC_EXTI_LINE_NUM); #endif /* defined(RTC_EXTI_LINE_NUM) */ } static int rtc_stm32_init(const struct device *dev) { const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); const struct rtc_stm32_config *cfg = dev->config; struct rtc_stm32_data *data = dev->data; int ret = -EIO; data->callback = NULL; /* Enable RTC bus clock */ if (clock_control_on(clk, (clock_control_subsys_t) &cfg->pclken[0]) != 0) { LOG_ERR("clock op failed"); return -EIO; } /* Enable Backup access */ z_stm32_hsem_lock(CFG_HW_RCC_SEMID, HSEM_LOCK_DEFAULT_RETRY); stm32_backup_domain_enable_access(); #if DT_INST_CLOCKS_CELL_BY_IDX(0, 1, bus) == STM32_SRC_HSE /* Must be configured before selecting the RTC clock source */ LL_RCC_SetRTC_HSEPrescaler(cfg->hse_prescaler); #endif /* Enable RTC clock source */ if (clock_control_configure(clk, (clock_control_subsys_t) &cfg->pclken[1], NULL) != 0) { LOG_ERR("clock configure failed"); goto out_disable_bkup_access; } #if !defined(CONFIG_SOC_SERIES_STM32WBAX) #ifdef CONFIG_SOC_SERIES_STM32U3X /* STM32U3 series uses LL_RCC_RTC_ClockEnable instead of LL_RCC_EnableRTC */ LL_RCC_RTC_ClockEnable(); #else LL_RCC_EnableRTC(); #endif /* CONFIG_SOC_SERIES_STM32U3X */ #endif /* !CONFIG_SOC_SERIES_STM32WBAX */ z_stm32_hsem_unlock(CFG_HW_RCC_SEMID); #if !defined(CONFIG_COUNTER_RTC_STM32_SAVE_VALUE_BETWEEN_RESETS) ret = rtc_stm32_deinit(); if (ret < 0) { LOG_ERR("Failed to deinit RTC"); goto out_disable_bkup_access; } #endif ret = rtc_stm32_configure(dev); if (ret < 0) { LOG_ERR("Failed to init RTC"); goto out_disable_bkup_access; } #ifdef RTC_CR_BYPSHAD LL_RTC_DisableWriteProtection(STM32_ARG(RTC)); STM32_RTC_EnableBypassShadowReg(STM32_ARG(RTC)); LL_RTC_EnableWriteProtection(STM32_ARG(RTC)); #endif /* RTC_CR_BYPSHAD */ #if defined(RTC_EXTI_LINE_NUM) /* Trigger NVIC IRQ on RTC EXTI line rising edge */ ret = stm32_exti_enable(RTC_EXTI_LINE_NUM, STM32_EXTI_TRIG_RISING, STM32_EXTI_MODE_IT); if (ret < 0) { LOG_ERR("Failed to enable RTC EXTI line"); goto out_disable_bkup_access; } #endif /* defined(RTC_EXTI_LINE_NUM) */ out_disable_bkup_access: stm32_backup_domain_disable_access(); if (ret == 0) { rtc_stm32_irq_config(dev); } return ret; } static struct rtc_stm32_data rtc_data; static const struct stm32_pclken rtc_clk[] = STM32_DT_INST_CLOCKS(0); #if DT_INST_CLOCKS_CELL_BY_IDX(0, 1, bus) == STM32_SRC_HSE #if STM32_HSE_FREQ % MHZ(1) != 0 #error RTC clock source HSE frequency should be whole MHz #elif STM32_HSE_FREQ < MHZ(16) && defined(LL_RCC_RTC_HSE_DIV_16) #define RTC_HSE_PRESCALER LL_RCC_RTC_HSE_DIV_16 #define RTC_HSE_FREQUENCY (STM32_HSE_FREQ / 16) #elif STM32_HSE_FREQ < MHZ(32) && defined(LL_RCC_RTC_HSE_DIV_32) #define RTC_HSE_PRESCALER LL_RCC_RTC_HSE_DIV_32 #define RTC_HSE_FREQUENCY (STM32_HSE_FREQ / 32) #elif STM32_HSE_FREQ < MHZ(64) && defined(LL_RCC_RTC_HSE_DIV_64) #define RTC_HSE_PRESCALER LL_RCC_RTC_HSE_DIV_64 #define RTC_HSE_FREQUENCY (STM32_HSE_FREQ / 64) #else #error RTC does not support HSE frequency #endif #define RTC_HSE_ASYNC_PRESCALER 125 #define RTC_HSE_SYNC_PRESCALER (RTC_HSE_FREQUENCY / RTC_HSE_ASYNC_PRESCALER) #endif /* DT_INST_CLOCKS_CELL_BY_IDX(0, 1, bus) == STM32_SRC_HSE */ static const struct rtc_stm32_config rtc_config = { .counter_info = { .max_top_value = UINT32_MAX, #ifndef CONFIG_COUNTER_RTC_STM32_SUBSECONDS /* freq = 1Hz for not subsec based driver */ .freq = RTCCLK_FREQ / ((RTC_ASYNCPRE + 1) * (RTC_SYNCPRE + 1)), #else /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ .freq = RTCCLK_FREQ / (RTC_ASYNCPRE + 1), #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ .flags = COUNTER_CONFIG_INFO_COUNT_UP, .channels = 1, }, #if DT_INST_CLOCKS_CELL_BY_IDX(0, 1, bus) == STM32_SRC_LSI || \ DT_INST_CLOCKS_CELL_BY_IDX(0, 1, bus) == STM32_SRC_LSE .async_prescaler = DT_INST_PROP_OR(0, async_prescaler, RTC_ASYNCPRE), #if !defined(CONFIG_SOC_SERIES_STM32F1X) .sync_prescaler = DT_INST_PROP_OR(0, sync_prescaler, RTC_SYNCPRE), #endif /* !CONFIG_SOC_SERIES_STM32F1X */ #elif DT_INST_CLOCKS_CELL_BY_IDX(0, 1, bus) == STM32_SRC_HSE .async_prescaler = DT_INST_PROP_OR(0, async_prescaler, RTC_HSE_ASYNC_PRESCALER - 1), #if !defined(CONFIG_SOC_SERIES_STM32F1X) .sync_prescaler = DT_INST_PROP_OR(0, hse_prescaler, RTC_HSE_SYNC_PRESCALER - 1), #endif /* !CONFIG_SOC_SERIES_STM32F1X */ #else #error Invalid RTC SRC #endif .pclken = rtc_clk, #if DT_INST_CLOCKS_CELL_BY_IDX(0, 1, bus) == STM32_SRC_HSE .hse_prescaler = DT_INST_PROP_OR(0, hse_prescaler, RTC_HSE_PRESCALER), #endif }; #ifdef CONFIG_PM_DEVICE static int rtc_stm32_pm_action(const struct device *dev, enum pm_device_action action) { const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); const struct rtc_stm32_config *cfg = dev->config; switch (action) { case PM_DEVICE_ACTION_RESUME: /* Enable RTC bus clock */ if (clock_control_on(clk, (clock_control_subsys_t) &cfg->pclken[0]) != 0) { LOG_ERR("clock op failed"); return -EIO; } break; case PM_DEVICE_ACTION_SUSPEND: break; default: return -ENOTSUP; } return 0; } #endif /* CONFIG_PM_DEVICE */ static DEVICE_API(counter, rtc_stm32_driver_api) = { .start = rtc_stm32_start, .stop = rtc_stm32_stop, .get_value = rtc_stm32_get_value, #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS .get_value_64 = rtc_stm32_get_value_64, #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ .set_alarm = rtc_stm32_set_alarm, .cancel_alarm = rtc_stm32_cancel_alarm, .set_top_value = rtc_stm32_set_top_value, .get_pending_int = rtc_stm32_get_pending_int, .get_top_value = rtc_stm32_get_top_value, }; PM_DEVICE_DT_INST_DEFINE(0, rtc_stm32_pm_action); DEVICE_DT_INST_DEFINE(0, &rtc_stm32_init, PM_DEVICE_DT_INST_GET(0), &rtc_data, &rtc_config, PRE_KERNEL_1, CONFIG_COUNTER_INIT_PRIORITY, &rtc_stm32_driver_api); static void rtc_stm32_irq_config(const struct device *dev) { IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), rtc_stm32_isr, DEVICE_DT_INST_GET(0), 0); irq_enable(DT_INST_IRQN(0)); }