diff --git a/drivers/counter/Kconfig.stm32_rtc b/drivers/counter/Kconfig.stm32_rtc index 03e72babc60..0e1268557df 100644 --- a/drivers/counter/Kconfig.stm32_rtc +++ b/drivers/counter/Kconfig.stm32_rtc @@ -44,4 +44,13 @@ config COUNTER_RTC_STM32_SAVE_VALUE_BETWEEN_RESETS help Keep the counter value after each reset. +config COUNTER_RTC_STM32_SUBSECONDS + bool "Use the subseconds as a basic tick." + depends on !SOC_SERIES_STM32F1X + help + Use the subseconds as the basic time tick. It increases resolution + of the counter. The frequency of the time is RTC Source Clock divided + by 2. It is the clock after the first asynchronous prescaler. + The config increases power consumption. + endif # COUNTER_RTC_STM32 diff --git a/drivers/counter/counter_ll_stm32_rtc.c b/drivers/counter/counter_ll_stm32_rtc.c index 19be15a87e1..efb7cf61f8b 100644 --- a/drivers/counter/counter_ll_stm32_rtc.c +++ b/drivers/counter/counter_ll_stm32_rtc.c @@ -70,7 +70,12 @@ LOG_MODULE_REGISTER(counter_rtc_stm32, CONFIG_COUNTER_LOG_LEVEL); #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 */ @@ -78,6 +83,12 @@ LOG_MODULE_REGISTER(counter_rtc_stm32, CONFIG_COUNTER_LOG_LEVEL); /* 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; LL_RTC_InitTypeDef ll_rtc_config; @@ -88,6 +99,9 @@ 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 ErrorStatus ll_func_init_alarm(RTC_TypeDef *rtc, uint32_t format, @@ -136,6 +150,17 @@ static inline void ll_func_disable_interrupt_alarm(RTC_TypeDef *rtc) #endif } +#ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS +static inline uint32_t ll_func_isenabled_interrupt_alarm(RTC_TypeDef *rtc) +{ +#if defined(CONFIG_SOC_SERIES_STM32F1X) + return LL_RTC_IsEnabledIT_ALR(rtc); +#else + return LL_RTC_IsEnabledIT_ALRA(rtc); +#endif +} +#endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ + static inline void ll_func_enable_alarm(RTC_TypeDef *rtc) { #if defined(CONFIG_SOC_SERIES_STM32F1X) @@ -180,13 +205,16 @@ static int rtc_stm32_stop(const struct device *dev) return 0; } - -static uint32_t rtc_stm32_read(const struct device *dev) +tick_t rtc_stm32_read(const struct device *dev) { #if !defined(COUNTER_NO_DATE) struct tm now = { 0 }; time_t ts; - uint32_t rtc_date, rtc_time, ticks; + uint32_t rtc_date, rtc_time; + tick_t ticks; +#ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS + uint32_t rtc_subseconds; +#endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ #else uint32_t rtc_time, ticks; #endif @@ -198,7 +226,15 @@ static uint32_t rtc_stm32_read(const struct device *dev) #if !defined(COUNTER_NO_DATE) do { rtc_date = LL_RTC_DATE_Get(RTC); + +#ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS + do { + rtc_time = LL_RTC_TIME_Get(RTC); + rtc_subseconds = LL_RTC_TIME_GetSubSecond(RTC); + } while (rtc_time != LL_RTC_TIME_Get(RTC)); +#else /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ rtc_time = LL_RTC_TIME_Get(RTC); +#endif } while (rtc_date != LL_RTC_DATE_Get(RTC)); #else @@ -225,7 +261,16 @@ static uint32_t rtc_stm32_read(const struct device *dev) 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_subseconds; +#endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ + #else ticks = rtc_time; #endif @@ -234,25 +279,43 @@ static uint32_t rtc_stm32_read(const struct device *dev) } 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) +{ + NVIC_SetPendingIRQ(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; + 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 LL_RTC_AlarmTypeDef rtc_alarm; struct rtc_stm32_data *data = dev->data; - uint32_t now = rtc_stm32_read(dev); - uint32_t ticks = alarm_cfg->ticks; + tick_t now = rtc_stm32_read(dev); + tick_t ticks = alarm_cfg->ticks; if (data->callback != NULL) { LOG_DBG("Alarm busy\n"); @@ -271,10 +334,14 @@ static int rtc_stm32_set_alarm(const struct device *dev, uint8_t chan_id, * that tick+1 event occurs before alarm setting is finished. */ ticks += now + 1; - alarm_val = (time_t)(ticks / counter_get_frequency(dev)) + T_TIME_OFFSET; + alarm_val_s = (time_t)(ticks / counter_get_frequency(dev)) + T_TIME_OFFSET; } else { - alarm_val = (time_t)(ticks / counter_get_frequency(dev)); + alarm_val_s = (time_t)(ticks / counter_get_frequency(dev)); } +#ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS + alarm_val_ss = ticks % counter_get_frequency(dev); +#endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ + #else if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) { remain = ticks + now + 1; @@ -289,9 +356,13 @@ static int rtc_stm32_set_alarm(const struct device *dev, uint8_t chan_id, #endif #if !defined(COUNTER_NO_DATE) +#ifndef CONFIG_COUNTER_RTC_STM32_SUBSECONDS LOG_DBG("Set Alarm: %d\n", ticks); +#else /* !CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ + LOG_DBG("Set Alarm: %llu\n", ticks); +#endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ - gmtime_r(&alarm_val, &alarm_tm); + gmtime_r(&alarm_val_s, &alarm_tm); /* Apply ALARM_A */ rtc_alarm.AlarmTime.TimeFormat = LL_RTC_TIME_FORMAT_AM_OR_24; @@ -319,11 +390,34 @@ static int rtc_stm32_set_alarm(const struct device *dev, uint8_t chan_id, } LL_RTC_DisableWriteProtection(RTC); +#ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS + /* Care about all bits of the subsecond register */ + LL_RTC_ALMA_SetSubSecondMask(RTC, 0xF); + LL_RTC_ALMA_SetSubSecond(RTC, RTC_SYNCPRE - alarm_val_ss); +#endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ ll_func_enable_alarm(RTC); ll_func_clear_alarm_flag(RTC); ll_func_enable_interrupt_alarm(RTC); LL_RTC_EnableWriteProtection(RTC); +#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 = 1; + rtc_stm32_set_int_pending(); + } +#endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ + return 0; } @@ -380,13 +474,20 @@ void rtc_stm32_isr(const struct device *dev) uint32_t now = rtc_stm32_read(dev); - if (ll_func_is_active_alarm(RTC) != 0) { + if (ll_func_is_active_alarm(RTC) != 0 +#ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS + || (data->irq_on_late && ll_func_isenabled_interrupt_alarm(RTC)) +#endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ + ) { LL_RTC_DisableWriteProtection(RTC); ll_func_clear_alarm_flag(RTC); ll_func_disable_interrupt_alarm(RTC); ll_func_disable_alarm(RTC); LL_RTC_EnableWriteProtection(RTC); +#ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS + data->irq_on_late = 0; +#endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ if (alarm_callback != NULL) { data->callback = NULL; @@ -500,7 +601,12 @@ static const struct stm32_pclken rtc_clk[] = STM32_DT_INST_CLOCKS(0); 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, }, @@ -521,6 +627,9 @@ static const struct counter_driver_api 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, diff --git a/tests/drivers/counter/counter_basic_api/testcase.yaml b/tests/drivers/counter/counter_basic_api/testcase.yaml index f31fa8f3af9..6e68f4480f4 100644 --- a/tests/drivers/counter/counter_basic_api/testcase.yaml +++ b/tests/drivers/counter/counter_basic_api/testcase.yaml @@ -18,3 +18,12 @@ tests: - CONFIG_ZERO_LATENCY_IRQS=y extra_args: DTC_OVERLAY_FILE="boards/nrf52840dk_nrf52840.overlay;boards/nrf52840dk_nrf52840_zli.overlay" + drivers.counter.basic_api.stm32_subsec: + tags: + - drivers + - counter + depends_on: counter + platform_allow: nucleo_f429zi + timeout: 600 + extra_configs: + - CONFIG_COUNTER_RTC_STM32_SUBSECONDS=y