diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index 5dd2e062e97..6301c23e8d3 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -9,5 +9,6 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_RTC counter_mcux_rtc zephyr_library_sources_ifdef(CONFIG_COUNTER_NRF_TIMER counter_nrfx_timer.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_NRF_RTC counter_nrfx_rtc.c) zephyr_library_sources_ifdef(CONFIG_RTC_QMSI counter_rtc_qmsi.c) +zephyr_library_sources_ifdef(CONFIG_COUNTER_RTC_STM32 counter_ll_stm32_rtc.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE counter_handlers.c) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 77467461e7f..0a0e8cd93e0 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -32,4 +32,6 @@ source "drivers/counter/Kconfig.nrfx" source "drivers/counter/Kconfig.imx_epit" +source "drivers/counter/Kconfig.stm32_rtc" + endif # COUNTER diff --git a/drivers/counter/Kconfig.stm32_rtc b/drivers/counter/Kconfig.stm32_rtc new file mode 100644 index 00000000000..7190c614c79 --- /dev/null +++ b/drivers/counter/Kconfig.stm32_rtc @@ -0,0 +1,70 @@ +# Kconfig - STM32 Cube LL RTC +# +# Copyright (c) 2018, Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig COUNTER_RTC_STM32 + bool "STM32 Counter RTC driver" + depends on SOC_FAMILY_STM32 + select USE_STM32_LL_RTC + select USE_STM32_LL_PWR + select USE_STM32_LL_RCC + select USE_STM32_LL_EXTI + select NEWLIB_LIBC + help + Build RTC driver for STM32 SoCs. Tested on STM32 F3, F4, L4, F7 series + +choice COUNTER_RTC_STM32_CLOCK_SRC + bool "RTC clock source" + depends on COUNTER_RTC_STM32 + +config COUNTER_RTC_STM32_CLOCK_LSI + bool "LSI" + help + Use LSI as RTC clock + +config COUNTER_RTC_STM32_CLOCK_LSE + bool "LSE" + help + Use LSE as RTC clock + +endchoice #COUNTER_RTC_STM32_CLOCK_SRC + +if !SOC_SERIES_STM32F4X + +choice COUNTER_RTC_STM32_LSE_DRIVE + prompt "LSE oscillator drive capability" + depends on RTC_STM32_CLOCK_LSE + +config COUNTER_RTC_STM32_LSE_DRIVE_LOW + bool "Low" + help + Xtal mode lower driving capability + +config COUNTER_RTC_STM32_LSE_DRIVE_MEDIUMLOW + bool "Medium Low" + help + Xtal mode medium low driving capability + +config COUNTER_RTC_STM32_LSE_DRIVE_MEDIUMHIGH + bool "Medium High" + help + Xtal mode medium high driving capability + +config COUNTER_RTC_STM32_LSE_DRIVE_HIGH + bool "High" + help + Xtal mode higher driving capability + +endchoice + +config COUNTER_RTC_STM32_LSE_DRIVE_STRENGTH + hex + default 0x00000000 if COUNTER_RTC_STM32_LSE_DRIVE_LOW + default 0x00000008 if COUNTER_RTC_STM32_LSE_DRIVE_MEDIUMLOW + default 0x00000010 if COUNTER_RTC_STM32_LSE_DRIVE_MEDIUMHIGH + default 0x00000018 if COUNTER_RTC_STM32_LSE_DRIVE_HIGH + +endif diff --git a/drivers/counter/counter_ll_stm32_rtc.c b/drivers/counter/counter_ll_stm32_rtc.c new file mode 100644 index 00000000000..72a635649f6 --- /dev/null +++ b/drivers/counter/counter_ll_stm32_rtc.c @@ -0,0 +1,362 @@ +/* + * 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 + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(counter_rtc_stm32, CONFIG_COUNTER_LOG_LEVEL); + +#define T_TIME_OFFSET 2088656896 + +#if defined(CONFIG_SOC_SERIES_STM32L4X) +#define RTC_EXTI_LINE LL_EXTI_LINE_18 +#elif defined(CONFIG_SOC_SERIES_STM32F4X) \ + || defined(CONFIG_SOC_SERIES_STM32F3X) \ + || defined(CONFIG_SOC_SERIES_STM32F7X) +#define RTC_EXTI_LINE LL_EXTI_LINE_17 +#endif + +struct rtc_stm32_config { + struct counter_config_info counter_info; + struct stm32_pclken pclken; + LL_RTC_InitTypeDef ll_rtc_config; +}; + +struct rtc_stm32_data { + counter_alarm_callback_t callback; + u32_t ticks; + void *user_data; + bool absolute; +}; + + +#define DEV_DATA(dev) ((struct rtc_stm32_data *)(dev)->driver_data) +#define DEV_CFG(dev) \ +((const struct rtc_stm32_config * const)(dev)->config->config_info) + + +static void rtc_stm32_irq_config(struct device *dev); + + +static int rtc_stm32_start(struct device *dev) +{ + ARG_UNUSED(dev); + + LL_RCC_EnableRTC(); + + return 0; +} + + +static int rtc_stm32_stop(struct device *dev) +{ + ARG_UNUSED(dev); + + LL_RCC_DisableRTC(); + + return 0; +} + + +static u32_t rtc_stm32_read(struct device *dev) +{ + struct tm now = { 0 }; + time_t ts; + u32_t rtc_date, rtc_time, ticks; + + ARG_UNUSED(dev); + + /* Read time and date registers */ + rtc_time = LL_RTC_TIME_Get(RTC); + rtc_date = LL_RTC_DATE_Get(RTC); + + /* Convert calendar datetime to UNIX timestamp */ + now.tm_year = __LL_RTC_CONVERT_BCD2BIN( + __LL_RTC_GET_YEAR(rtc_date)); + now.tm_mon = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_MONTH(rtc_date)); + now.tm_mday = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_DAY(rtc_date)); + + now.tm_hour = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_HOUR(rtc_time)); + now.tm_min = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_MINUTE(rtc_time)); + now.tm_sec = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_SECOND(rtc_time)); + + ts = mktime(&now); + + /* Return number of seconds since RTC init */ + ts -= T_TIME_OFFSET; + + ticks = counter_us_to_ticks(dev, (u64_t)(ts * USEC_PER_SEC)); + + return ticks; +} + +static int rtc_stm32_set_alarm(struct device *dev, u8_t chan_id, + const struct counter_alarm_cfg *alarm_cfg) +{ + struct tm alarm_tm; + time_t alarm_val; + LL_RTC_AlarmTypeDef rtc_alarm; + struct rtc_stm32_data *data = DEV_DATA(dev); + + u32_t now = rtc_stm32_read(dev); + u32_t ticks = alarm_cfg->ticks; + + if (data->callback != NULL) { + LOG_DBG("Alarm busy\n"); + return -EBUSY; + } + + + data->callback = alarm_cfg->callback; + data->user_data = alarm_cfg->user_data; + data->absolute = alarm_cfg->absolute; + + if (!alarm_cfg->absolute) { + ticks += now; + } + + LOG_DBG("Set Alarm: %d\n", ticks); + + alarm_val = (time_t)(counter_ticks_to_us(dev, ticks) / USEC_PER_SEC); + + gmtime_r(&alarm_val, &alarm_tm); + + /* Apply ALARM_A */ + rtc_alarm.AlarmTime.TimeFormat = LL_RTC_TIME_FORMAT_AM_OR_24; + rtc_alarm.AlarmTime.Hours = alarm_tm.tm_hour; + rtc_alarm.AlarmTime.Minutes = alarm_tm.tm_min; + rtc_alarm.AlarmTime.Seconds = alarm_tm.tm_sec; + + rtc_alarm.AlarmMask = LL_RTC_ALMA_MASK_NONE; + rtc_alarm.AlarmDateWeekDaySel = LL_RTC_ALMA_DATEWEEKDAYSEL_DATE; + rtc_alarm.AlarmDateWeekDay = alarm_tm.tm_mday; + + LL_RTC_DisableWriteProtection(RTC); + LL_RTC_ALMA_Disable(RTC); + LL_RTC_EnableWriteProtection(RTC); + + if (LL_RTC_ALMA_Init(RTC, LL_RTC_FORMAT_BIN, &rtc_alarm) != SUCCESS) { + return -EIO; + } + + LL_RTC_DisableWriteProtection(RTC); + LL_RTC_ALMA_Enable(RTC); + LL_RTC_ClearFlag_ALRA(RTC); + LL_RTC_EnableIT_ALRA(RTC); + LL_RTC_EnableWriteProtection(RTC); + + return 0; +} + + +static int rtc_stm32_disable_alarm(struct device *dev, u8_t chan_id) +{ + LL_RTC_DisableWriteProtection(RTC); + LL_RTC_ClearFlag_ALRA(RTC); + LL_RTC_DisableIT_ALRA(RTC); + LL_RTC_ALMA_Disable(RTC); + LL_RTC_EnableWriteProtection(RTC); + + DEV_DATA(dev)->callback = NULL; + + return 0; +} + + +static u32_t rtc_stm32_get_pending_int(struct device *dev) +{ + return LL_RTC_IsActiveFlag_ALRA(RTC) != 0; +} + + +static u32_t rtc_stm32_get_top_value(struct device *dev) +{ + const struct counter_config_info *info = dev->config->config_info; + + return info->max_top_value; +} + + +static int rtc_stm32_set_top_value(struct device *dev, u32_t ticks, + counter_top_callback_t callback, + void *user_data) +{ + const struct counter_config_info *info = dev->config->config_info; + + ARG_UNUSED(dev); + ARG_UNUSED(callback); + ARG_UNUSED(user_data); + + if (ticks != info->max_top_value) { + return -ENOTSUP; + } else { + return 0; + } + + +} + + +static u32_t rtc_stm32_get_max_relative_alarm(struct device *dev) +{ + const struct counter_config_info *info = dev->config->config_info; + + return info->max_top_value; +} + + +void rtc_stm32_isr(void *arg) +{ + struct device *const dev = (struct device *)arg; + struct rtc_stm32_data *data = DEV_DATA(dev); + counter_alarm_callback_t alarm_callback = data->callback; + + u32_t now = rtc_stm32_read(dev); + + if (LL_RTC_IsActiveFlag_ALRA(RTC) != 0) { + + LL_RTC_DisableWriteProtection(RTC); + LL_RTC_ClearFlag_ALRA(RTC); + LL_RTC_DisableIT_ALRA(RTC); + LL_RTC_ALMA_Disable(RTC); + LL_RTC_EnableWriteProtection(RTC); + + if (alarm_callback != NULL) { + data->callback = NULL; + alarm_callback(dev, 0, now, data->user_data); + } + } + + LL_EXTI_ClearFlag_0_31(RTC_EXTI_LINE); +} + + +static int rtc_stm32_init(struct device *dev) +{ + struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME); + const struct rtc_stm32_config *cfg = DEV_CFG(dev); + + __ASSERT_NO_MSG(clk); + + DEV_DATA(dev)->callback = NULL; + + clock_control_on(clk, (clock_control_subsys_t *) &cfg->pclken); + + LL_PWR_EnableBkUpAccess(); + LL_RCC_ForceBackupDomainReset(); + LL_RCC_ReleaseBackupDomainReset(); + +#if defined(CONFIG_COUNTER_RTC_STM32_CLOCK_LSI) + + LL_RCC_LSI_Enable(); + while (LL_RCC_LSI_IsReady() != 1) + ; + LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI); + +#else /* CONFIG_COUNTER_RTC_STM32_CLOCK_LSE */ + +#ifndef(CONFIG_SOC_SERIES_STM32F4X) + LL_RCC_LSE_SetDriveCapability( + CONFIG_COUNTER_RTC_STM32_LSE_DRIVE_STRENGTH); +#endif /* !CONFIG_SOC_SERIES_STM32F4X */ + LL_RCC_LSE_Enable(); + + /* Wait until LSE is ready */ + while (LL_RCC_LSE_IsReady() != 1) + ; + + LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); + +#endif /* CONFIG_COUNTER_RTC_STM32_CLOCK_SRC */ + + LL_RCC_EnableRTC(); + + if (LL_RTC_DeInit(RTC) != SUCCESS) { + return -EIO; + } + + if (LL_RTC_Init(RTC, ((LL_RTC_InitTypeDef *) + &cfg->ll_rtc_config)) != SUCCESS) { + return -EIO; + } + +#ifdef RTC_CR_BYPSHAD + LL_RTC_DisableWriteProtection(RTC); + LL_RTC_EnableShadowRegBypass(RTC); + LL_RTC_EnableWriteProtection(RTC); +#endif /* RTC_CR_BYPSHAD */ + + LL_EXTI_EnableIT_0_31(RTC_EXTI_LINE); + LL_EXTI_EnableRisingTrig_0_31(RTC_EXTI_LINE); + + rtc_stm32_irq_config(dev); + + return 0; +} + +static struct rtc_stm32_data rtc_data; + +static const struct rtc_stm32_config rtc_config = { + .counter_info = { + .max_top_value = UINT32_MAX, + .freq = 1, + .count_up = true, + .channels = 1, + }, + .pclken = { + .enr = LL_APB1_GRP1_PERIPH_PWR, + .bus = STM32_CLOCK_BUS_APB1, + }, + .ll_rtc_config = { + .HourFormat = LL_RTC_HOURFORMAT_24HOUR, +#if defined(CONFIG_COUNTER_RTC_STM32_CLOCK_LSI) + /* prescaler values for LSI @ 32 KHz */ + .AsynchPrescaler = 0x7F, + .SynchPrescaler = 0x00F9, +#else /* CONFIG_COUNTER_RTC_STM32_CLOCK_LSE */ + /* prescaler values for LSE @ 32768 Hz */ + .AsynchPrescaler = 0x7F, + .SynchPrescaler = 0x00FF, +#endif + }, +}; + + +static const struct counter_driver_api rtc_stm32_driver_api = { + .start = rtc_stm32_start, + .stop = rtc_stm32_stop, + .read = rtc_stm32_read, + .set_alarm = rtc_stm32_set_alarm, + .disable_alarm = rtc_stm32_disable_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, + .get_max_relative_alarm = rtc_stm32_get_max_relative_alarm, +}; + +DEVICE_AND_API_INIT(rtc_stm32, DT_RTC_0_NAME, &rtc_stm32_init, + &rtc_data, &rtc_config, PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &rtc_stm32_driver_api); + +static void rtc_stm32_irq_config(struct device *dev) +{ + IRQ_CONNECT(DT_RTC_0_IRQ, CONFIG_RTC_0_IRQ_PRI, + rtc_stm32_isr, DEVICE_GET(rtc_stm32), 0); + irq_enable(DT_RTC_0_IRQ); +} diff --git a/soc/arm/st_stm32/common/Kconfig.defconfig.series b/soc/arm/st_stm32/common/Kconfig.defconfig.series index b104ee84a14..b90ce8b9da2 100644 --- a/soc/arm/st_stm32/common/Kconfig.defconfig.series +++ b/soc/arm/st_stm32/common/Kconfig.defconfig.series @@ -79,4 +79,11 @@ config RTC_STM32 endif # RTC +if COUNTER + +config COUNTER_RTC_STM32 + default y + +endif # COUNTER + endif # SOC_FAMILY_STM32 diff --git a/soc/arm/st_stm32/stm32f3/dts_fixup.h b/soc/arm/st_stm32/stm32f3/dts_fixup.h index c9490c1de33..607106743a7 100644 --- a/soc/arm/st_stm32/stm32f3/dts_fixup.h +++ b/soc/arm/st_stm32/stm32f3/dts_fixup.h @@ -253,6 +253,7 @@ #define CONFIG_RTC_0_IRQ_PRI DT_ST_STM32_RTC_40002800_IRQ_0_PRIORITY #define DT_RTC_0_IRQ DT_ST_STM32_RTC_40002800_IRQ_0 #define CONFIG_RTC_0_NAME DT_ST_STM32_RTC_40002800_LABEL +#define DT_RTC_0_NAME DT_ST_STM32_RTC_40002800_LABEL #define CONFIG_RTC_PRESCALER DT_ST_STM32_RTC_40002800_PRESCALER #define DT_WDT_0_NAME DT_ST_STM32_WATCHDOG_0_LABEL diff --git a/soc/arm/st_stm32/stm32f3/soc.h b/soc/arm/st_stm32/stm32f3/soc.h index 31f58ff213a..47f3a938b68 100644 --- a/soc/arm/st_stm32/stm32f3/soc.h +++ b/soc/arm/st_stm32/stm32f3/soc.h @@ -56,7 +56,7 @@ #include #endif -#ifdef CONFIG_RTC_STM32 +#if defined(CONFIG_RTC_STM32) || defined(CONFIG_COUNTER_RTC_STM32) #include #include #include diff --git a/soc/arm/st_stm32/stm32f4/dts_fixup.h b/soc/arm/st_stm32/stm32f4/dts_fixup.h index b27ceb2f891..801ac02ed7e 100644 --- a/soc/arm/st_stm32/stm32f4/dts_fixup.h +++ b/soc/arm/st_stm32/stm32f4/dts_fixup.h @@ -364,6 +364,7 @@ #define CONFIG_RTC_0_IRQ_PRI DT_ST_STM32_RTC_40002800_IRQ_0_PRIORITY #define DT_RTC_0_IRQ DT_ST_STM32_RTC_40002800_IRQ_0 #define CONFIG_RTC_0_NAME DT_ST_STM32_RTC_40002800_LABEL +#define DT_RTC_0_NAME DT_ST_STM32_RTC_40002800_LABEL #define CONFIG_RTC_PRESCALER DT_ST_STM32_RTC_40002800_PRESCALER #define DT_WDT_0_NAME DT_ST_STM32_WATCHDOG_0_LABEL diff --git a/soc/arm/st_stm32/stm32f4/soc.h b/soc/arm/st_stm32/stm32f4/soc.h index 432b84ea059..d0af3973850 100644 --- a/soc/arm/st_stm32/stm32f4/soc.h +++ b/soc/arm/st_stm32/stm32f4/soc.h @@ -59,7 +59,7 @@ #include #endif -#ifdef CONFIG_RTC_STM32 +#if defined(CONFIG_RTC_STM32) || defined(CONFIG_COUNTER_RTC_STM32) #include #include #include diff --git a/soc/arm/st_stm32/stm32f7/dts_fixup.h b/soc/arm/st_stm32/stm32f7/dts_fixup.h index eac2eebaca5..ab310508a89 100644 --- a/soc/arm/st_stm32/stm32f7/dts_fixup.h +++ b/soc/arm/st_stm32/stm32f7/dts_fixup.h @@ -353,6 +353,7 @@ #define CONFIG_RTC_0_IRQ_PRI DT_ST_STM32_RTC_40002800_IRQ_0_PRIORITY #define DT_RTC_0_IRQ DT_ST_STM32_RTC_40002800_IRQ_0 #define CONFIG_RTC_0_NAME DT_ST_STM32_RTC_40002800_LABEL +#define DT_RTC_0_NAME DT_ST_STM32_RTC_40002800_LABEL #define CONFIG_RTC_PRESCALER DT_ST_STM32_RTC_40002800_PRESCALER #define DT_FLASH_DEV_BASE_ADDRESS DT_ST_STM32F7_FLASH_CONTROLLER_40023C00_BASE_ADDRESS diff --git a/soc/arm/st_stm32/stm32f7/soc.h b/soc/arm/st_stm32/stm32f7/soc.h index 99d2c3b1ca8..287be586bcf 100644 --- a/soc/arm/st_stm32/stm32f7/soc.h +++ b/soc/arm/st_stm32/stm32f7/soc.h @@ -54,7 +54,7 @@ #include #endif -#ifdef CONFIG_RTC_STM32 +#if defined(CONFIG_RTC_STM32) || defined(CONFIG_COUNTER_RTC_STM32) #include #include #include diff --git a/soc/arm/st_stm32/stm32l4/dts_fixup.h b/soc/arm/st_stm32/stm32l4/dts_fixup.h index 868407efbd7..5499a6789ff 100644 --- a/soc/arm/st_stm32/stm32l4/dts_fixup.h +++ b/soc/arm/st_stm32/stm32l4/dts_fixup.h @@ -175,6 +175,7 @@ #define CONFIG_RTC_0_IRQ_PRI DT_ST_STM32_RTC_40002800_IRQ_0_PRIORITY #define DT_RTC_0_IRQ DT_ST_STM32_RTC_40002800_IRQ_0 #define CONFIG_RTC_0_NAME DT_ST_STM32_RTC_40002800_LABEL +#define DT_RTC_0_NAME DT_ST_STM32_RTC_40002800_LABEL #define CONFIG_RTC_PRESCALER DT_ST_STM32_RTC_40002800_PRESCALER #define DT_SPI_1_BASE_ADDRESS DT_ST_STM32_SPI_FIFO_40013000_BASE_ADDRESS diff --git a/soc/arm/st_stm32/stm32l4/soc.h b/soc/arm/st_stm32/stm32l4/soc.h index 940a54b271a..19321ca5620 100644 --- a/soc/arm/st_stm32/stm32l4/soc.h +++ b/soc/arm/st_stm32/stm32l4/soc.h @@ -68,7 +68,7 @@ #include #endif -#ifdef CONFIG_RTC_STM32 +#if defined(CONFIG_RTC_STM32) || defined(CONFIG_COUNTER_RTC_STM32) #include #include #include