diff --git a/CODEOWNERS b/CODEOWNERS index 6b0bd21c2cf7..859f3cfe61be 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -186,6 +186,7 @@ /drivers/pinmux/stm32/ @rsalveti @idlethread /drivers/pinmux/*hsdk* @iriszzw /drivers/pwm/*litex* @mateusz-holenko @kgugala @pgielda +/drivers/rtc/ @jdascenzio /drivers/sensor/ @MaureenHelm /drivers/sensor/ams_iAQcore/ @alexanderwachter /drivers/sensor/ens210/ @alexanderwachter diff --git a/boards/arm/disco_l475_iot1/disco_l475_iot1.yaml b/boards/arm/disco_l475_iot1/disco_l475_iot1.yaml index 0a5e402e44aa..6291a267c13a 100644 --- a/boards/arm/disco_l475_iot1/disco_l475_iot1.yaml +++ b/boards/arm/disco_l475_iot1/disco_l475_iot1.yaml @@ -20,5 +20,6 @@ supported: - nvs - vl53l0x - watchdog + - rtc ram: 96 flash: 1024 diff --git a/boards/arm/nucleo_l432kc/nucleo_l432kc.yaml b/boards/arm/nucleo_l432kc/nucleo_l432kc.yaml index 7703e8532f3a..d14a6c3af411 100644 --- a/boards/arm/nucleo_l432kc/nucleo_l432kc.yaml +++ b/boards/arm/nucleo_l432kc/nucleo_l432kc.yaml @@ -14,3 +14,4 @@ supported: - can - counter - spi + - rtc diff --git a/boards/arm/nucleo_l452re/nucleo_l452re.yaml b/boards/arm/nucleo_l452re/nucleo_l452re.yaml index 8bc79f9f0769..4d96880bbc6e 100644 --- a/boards/arm/nucleo_l452re/nucleo_l452re.yaml +++ b/boards/arm/nucleo_l452re/nucleo_l452re.yaml @@ -14,3 +14,4 @@ supported: - can - counter - spi + - rtc diff --git a/boards/arm/nucleo_l476rg/nucleo_l476rg.yaml b/boards/arm/nucleo_l476rg/nucleo_l476rg.yaml index 676e5fcf80fd..11d1adbae707 100644 --- a/boards/arm/nucleo_l476rg/nucleo_l476rg.yaml +++ b/boards/arm/nucleo_l476rg/nucleo_l476rg.yaml @@ -15,5 +15,6 @@ supported: - i2c - counter - adc + - rtc ram: 96 flash: 1024 diff --git a/boards/arm/nucleo_l496zg/nucleo_l496zg.yaml b/boards/arm/nucleo_l496zg/nucleo_l496zg.yaml index b7293ed6ca16..5246859682b4 100644 --- a/boards/arm/nucleo_l496zg/nucleo_l496zg.yaml +++ b/boards/arm/nucleo_l496zg/nucleo_l496zg.yaml @@ -17,6 +17,7 @@ supported: - pwm - counter - watchdog + - rtc testing: ignore_tags: - net diff --git a/boards/arm/nucleo_l4r5zi/nucleo_l4r5zi.yaml b/boards/arm/nucleo_l4r5zi/nucleo_l4r5zi.yaml index e50f1180c442..dd422448d74c 100644 --- a/boards/arm/nucleo_l4r5zi/nucleo_l4r5zi.yaml +++ b/boards/arm/nucleo_l4r5zi/nucleo_l4r5zi.yaml @@ -17,5 +17,6 @@ supported: - usb device - nvs - counter + - rtc ram: 640 flash: 2048 diff --git a/boards/arm/sensortile_box/sensortile_box.yaml b/boards/arm/sensortile_box/sensortile_box.yaml index 34aea20266fe..9f095d280f91 100644 --- a/boards/arm/sensortile_box/sensortile_box.yaml +++ b/boards/arm/sensortile_box/sensortile_box.yaml @@ -14,5 +14,6 @@ supported: - usb device - nvs - counter + - rtc ram: 640 flash: 2048 diff --git a/boards/arm/stm32l476g_disco/stm32l476g_disco.yaml b/boards/arm/stm32l476g_disco/stm32l476g_disco.yaml index 42b03d58aafb..ac83b1bae693 100644 --- a/boards/arm/stm32l476g_disco/stm32l476g_disco.yaml +++ b/boards/arm/stm32l476g_disco/stm32l476g_disco.yaml @@ -11,3 +11,4 @@ flash: 1024 supported: - gpio - counter + - rtc diff --git a/boards/arm/stm32l496g_disco/stm32l496g_disco.yaml b/boards/arm/stm32l496g_disco/stm32l496g_disco.yaml index b6c468fbafd6..0ff13c7adbf1 100644 --- a/boards/arm/stm32l496g_disco/stm32l496g_disco.yaml +++ b/boards/arm/stm32l496g_disco/stm32l496g_disco.yaml @@ -15,3 +15,4 @@ supported: - spi - gpio - counter + - rtc diff --git a/drivers/Kconfig b/drivers/Kconfig index 4ca092c44fae..4e8ddd0211b9 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -59,6 +59,8 @@ source "drivers/sensor/Kconfig" source "drivers/counter/Kconfig" +source "drivers/rtc/Kconfig" + source "drivers/dma/Kconfig" source "drivers/usb/Kconfig" diff --git a/drivers/rtc/CMakeLists.txt b/drivers/rtc/CMakeLists.txt new file mode 100644 index 000000000000..c2597c14b275 --- /dev/null +++ b/drivers/rtc/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_RTC_STM32 rtc_stm32.c) + +zephyr_library_sources_ifdef(CONFIG_USERSPACE rtc_handlers.c) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig new file mode 100644 index 000000000000..3329d1b06a5f --- /dev/null +++ b/drivers/rtc/Kconfig @@ -0,0 +1,17 @@ +# Copyright (c) 2020 Paratronic +# SPDX-License-Identifier: Apache-2.0 + +menuconfig RTC + bool "RTC Drivers" + help + Enable support for RTC. + +if RTC + +module = RTC +module-str = rtc +source "subsys/logging/Kconfig.template.log_config" + +source "drivers/rtc/Kconfig.rtc_stm32" + +endif # RTC diff --git a/drivers/rtc/Kconfig.rtc_stm32 b/drivers/rtc/Kconfig.rtc_stm32 new file mode 100644 index 000000000000..795bdbf3ac0a --- /dev/null +++ b/drivers/rtc/Kconfig.rtc_stm32 @@ -0,0 +1,25 @@ +# Copyright (c) 2020 Paratronic +# SPDX-License-Identifier: Apache-2.0 + +menuconfig RTC_STM32 + bool "STM32 RTC Driver" + depends on SOC_FAMILY_STM32 && !COUNTER_RTC_STM32 + select USE_STM32_LL_RTC + help + Build RTC driver for STM32 SoCs + +choice RTC_STM32_CLOCK_SRC + bool "RTC clock source" + depends on RTC_STM32 + +config RTC_STM32_CLOCK_LSI + bool "LSI" + help + Use LSI as RTC clock + +config RTC_STM32_CLOCK_LSE + bool "LSE" + help + Use LSE as RTC clock + +endchoice #RTC_STM32_CLOCK_SRC diff --git a/drivers/rtc/rtc_handlers.c b/drivers/rtc/rtc_handlers.c new file mode 100644 index 000000000000..38e67653654a --- /dev/null +++ b/drivers/rtc/rtc_handlers.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 Paratronic + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +static inline int z_vrfy_rtc_get_time(struct device *dev, + struct timespec *tp) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_RTC)); + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(tp, sizeof(*tp))); + return z_impl_rtc_get_time(dev, tp); +} +#include + +static inline int z_vrfy_rtc_set_time(struct device *dev, + const struct timespec *tp) +{ + Z_OOPS(Z_SYSCALL_DRIVER_RTC(dev, set_time)); + Z_OOPS(Z_SYSCALL_MEMORY_READ(tp, sizeof(*tp))); + return z_impl_rtc_set_time(dev, tp); +} +#include diff --git a/drivers/rtc/rtc_stm32.c b/drivers/rtc/rtc_stm32.c new file mode 100644 index 000000000000..cd704937e4b7 --- /dev/null +++ b/drivers/rtc/rtc_stm32.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2020 Paratronic + * SPDX-License-Identifier: Apache-2.0 + * + * Source file for the STM32 RTC driver + * + */ + + +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_RTC_LOG_LEVEL +#include +LOG_MODULE_REGISTER(rtc); + +#if defined(CONFIG_RTC_STM32_CLOCK_LSI) +/* prescaler values for LSI @ 32 KHz */ +#define RTC_PREDIV_ASYNC 31 +#define RTC_PREDIV_SYNC 999 +#else /* CONFIG_RTC_STM32_CLOCK_LSE */ +/* prescaler values for LSE @ 32768 Hz */ +#define RTC_PREDIV_ASYNC 127 +#define RTC_PREDIV_SYNC 255 +#endif + +static int rtc_stm32_set_time(struct device *dev, const struct timespec *tp) +{ + int err = -EIO; + struct tm tm; + LL_RTC_TimeTypeDef timeDef; + LL_RTC_DateTypeDef dateDef; + const char *serr = NULL; + int key; + + ARG_UNUSED(dev); + + key = irq_lock(); + + LL_PWR_EnableBkUpAccess(); + + gmtime_r(&tp->tv_sec, &tm); + timeDef.Hours = tm.tm_hour; + timeDef.Minutes = tm.tm_min; + timeDef.Seconds = tm.tm_sec; + if (LL_RTC_TIME_Init(RTC, LL_RTC_FORMAT_BIN, &timeDef) != SUCCESS) { + serr = "failed to set time"; + goto out; + } + + dateDef.WeekDay = ((tm.tm_wday + 6) % 7) + 1; + dateDef.Month = tm.tm_mon + 1; + dateDef.Day = tm.tm_mday; + dateDef.Year = (tm.tm_year + 1900) - 2000; + if (LL_RTC_DATE_Init(RTC, LL_RTC_FORMAT_BIN, &dateDef) != SUCCESS) { + serr = "failed to set date"; + goto out; + } + + err = 0; +out: + LL_PWR_DisableBkUpAccess(); + + irq_unlock(key); + + if (serr) { + LOG_ERR("%s", serr); + } + + return err; +} + +static int rtc_stm32_get_time(struct device *dev, struct timespec *tp) +{ + struct tm tm; + u32_t time; + u32_t subSeconds; + u32_t date; + int key; + + ARG_UNUSED(dev); + + key = irq_lock(); + subSeconds = LL_RTC_TIME_GetSubSecond(RTC); + time = LL_RTC_TIME_Get(RTC); + date = LL_RTC_DATE_Get(RTC); + irq_unlock(key); + + tm.tm_sec = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_SECOND(time)); + tm.tm_min = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_MINUTE(time)); + tm.tm_hour = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_HOUR(time)); + + tm.tm_mday = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_DAY(date)); + tm.tm_mon = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_MONTH(date)) - 1; + tm.tm_year = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_YEAR(date)) + 100; + tp->tv_sec = timeutil_timegm(&tm); + tp->tv_nsec = ((u64_t)(RTC_PREDIV_SYNC - subSeconds) * NSEC_PER_SEC) / + (RTC_PREDIV_SYNC + 1); + + return 0; +} + +static int rtc_stm32_init(struct device *dev) +{ + int err = -EIO; + struct device *clk; + struct stm32_pclken pclken; + LL_RTC_InitTypeDef init; + + clk = device_get_binding(STM32_CLOCK_CONTROL_NAME); + __ASSERT_NO_MSG(clk); + + pclken.enr = DT_INST_0_ST_STM32_RTC_CLOCK_BITS; + pclken.bus = DT_INST_0_ST_STM32_RTC_CLOCK_BUS; + clock_control_on(clk, (clock_control_subsys_t *) &pclken); + + LL_PWR_EnableBkUpAccess(); + +#if defined(CONFIG_RTC_STM32_CLOCK_LSI) + LL_RCC_LSI_Enable(); + + /* Wait until LSI is ready */ + while (LL_RCC_LSI_IsReady() != 1) { + } + + LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI); + +#else /* CONFIG_RTC_STM32_CLOCK_LSE */ + LL_RCC_LSE_Enable(); + + /* Wait until LSE is ready */ + while (LL_RCC_LSE_IsReady() != 1) { + } + + LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); + +#endif + + LL_RCC_EnableRTC(); + + init.HourFormat = RTC_HOURFORMAT_24; + init.AsynchPrescaler = RTC_PREDIV_ASYNC; + init.SynchPrescaler = RTC_PREDIV_SYNC; + if (LL_RTC_Init(RTC, &init) != SUCCESS) { + LOG_ERR("failed to init"); + goto out; + } + /* check if calendar has been initialized */ + if (!LL_RTC_IsActiveFlag_INITS(RTC)) { + LOG_INF("Datetime initialization"); + struct timespec tp; + /* Init date time to 1 January 2001 00:00:00 */ + tp.tv_sec = 978307200; + tp.tv_nsec = 0; + err = rtc_stm32_set_time(dev, &tp); + if (err < 0) { + goto out; + } + } + + err = 0; +out: + LL_PWR_DisableBkUpAccess(); + if (err == 0) { + LOG_DBG("RTC initialised correctly"); + } + return err; +} + +static const struct rtc_driver_api rtc_stm32_driver_api = { + .get_time = rtc_stm32_get_time, + .set_time = rtc_stm32_set_time, +}; + +DEVICE_AND_API_INIT(rtc_stm32, DT_RTC_0_NAME, &rtc_stm32_init, + NULL, NULL, PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &rtc_stm32_driver_api); diff --git a/include/drivers/rtc.h b/include/drivers/rtc.h new file mode 100644 index 000000000000..3b3ae393cd3e --- /dev/null +++ b/include/drivers/rtc.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020 Paratronic + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_RTC_H_ +#define ZEPHYR_INCLUDE_DRIVERS_RTC_H_ + +/** + * @file + * @brief Public RTC driver APIs + */ + +#include +#include +#include + +typedef int (*rtc_api_get_time_t)(struct device *dev, + struct timespec *tp); +typedef int (*rtc_api_set_time_t)(struct device *dev, + const struct timespec *tp); + +__subsystem struct rtc_driver_api { + rtc_api_get_time_t get_time; + rtc_api_set_time_t set_time; +}; + +/** + * @brief Function to get RTC time + * + * @param[in] dev RTC device + * @param[out] tp pointer to where to store the current rtc value + * + * @return 0 on success, negative on error + */ +__syscall int rtc_get_time(struct device *dev, struct timespec *tp); + +static inline int z_impl_rtc_get_time(struct device *dev, struct timespec *tp) +{ + const struct rtc_driver_api *api = + (const struct rtc_driver_api *)dev->driver_api; + + return api->get_time(dev, tp); +} + +/** + * @brief Function to set RTC time + * + * @param[in] dev RTC device + * @param[in] tp pointer to new rtc value + * + * @return 0 on success, negative on error + */ +__syscall int rtc_set_time(struct device *dev, const struct timespec *tp); + +static inline int z_impl_rtc_set_time(struct device *dev, + const struct timespec *tp) +{ + const struct rtc_driver_api *api = + (const struct rtc_driver_api *)dev->driver_api; + + return api->set_time(dev, tp); +} + +#include + +#endif /* ZEPHYR_INCLUDE_DRIVERS_RTC_H_ */ diff --git a/lib/posix/Kconfig b/lib/posix/Kconfig index 3e9e85c4c59c..2e1568ddfada 100644 --- a/lib/posix/Kconfig +++ b/lib/posix/Kconfig @@ -48,6 +48,15 @@ config POSIX_CLOCK This enables POSIX clock\_\*(), timer\_\*(), and \*sleep() functions. +if POSIX_CLOCK + +config POSIX_CLOCK_RTC + bool "Use RTC for realtime clock" + depends on RTC + default n + +endif # POSIX_CLOCK + config MAX_TIMER_COUNT int "Maximum timer count in POSIX application" default 5 diff --git a/lib/posix/clock.c b/lib/posix/clock.c index 1a0ff64d7562..d0f3878bc853 100644 --- a/lib/posix/clock.c +++ b/lib/posix/clock.c @@ -7,6 +7,15 @@ #include #include #include +#include +#include +#include + +LOG_MODULE_REGISTER(posix_clock); + +#ifndef DT_RTC_0_NAME +#define DT_RTC_0_NAME "" +#endif /* * `k_uptime_get` returns a timestamp based on an always increasing @@ -17,6 +26,8 @@ */ static struct timespec rt_clock_base; +static struct device *rtc_dev; + /** * @brief Get clock time specified by clock_id. * @@ -34,6 +45,9 @@ int clock_gettime(clockid_t clock_id, struct timespec *ts) break; case CLOCK_REALTIME: + if (rtc_dev) { + return rtc_get_time(rtc_dev, ts); + } base = rt_clock_base; break; @@ -74,6 +88,11 @@ int clock_settime(clockid_t clock_id, const struct timespec *tp) return -1; } + if (rtc_dev) { + errno = rtc_set_time(rtc_dev, tp); + return (errno == 0) ? 0 : -1; + } + u64_t elapsed_msecs = k_uptime_get(); s64_t delta = (s64_t)NSEC_PER_SEC * tp->tv_sec + tp->tv_nsec - elapsed_msecs * USEC_PER_MSEC * NSEC_PER_USEC; @@ -106,3 +125,20 @@ int gettimeofday(struct timeval *tv, const void *tz) return res; } + +static int clock_init(struct device *unused) +{ + ARG_UNUSED(unused); + + if (!IS_ENABLED(CONFIG_POSIX_CLOCK_RTC)) + return 0; + + rtc_dev = device_get_binding(DT_RTC_0_NAME); + if (rtc_dev == NULL) { + LOG_ERR("Could not find RTC device"); + return -ENODEV; + } + return 0; +} + +SYS_INIT(clock_init, PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/soc/arm/st_stm32/stm32l4/Kconfig.defconfig.series b/soc/arm/st_stm32/stm32l4/Kconfig.defconfig.series index a1452365e0e5..fc2d66f10ac4 100644 --- a/soc/arm/st_stm32/stm32l4/Kconfig.defconfig.series +++ b/soc/arm/st_stm32/stm32l4/Kconfig.defconfig.series @@ -32,4 +32,8 @@ config DMA_STM32_V2 default y depends on DMA_STM32 +config RTC_STM32 + default y + depends on RTC + endif # SOC_SERIES_STM32L4X diff --git a/soc/arm/st_stm32/stm32l4/soc.h b/soc/arm/st_stm32/stm32l4/soc.h index 462c9f35d867..7d5c60184b7a 100644 --- a/soc/arm/st_stm32/stm32l4/soc.h +++ b/soc/arm/st_stm32/stm32l4/soc.h @@ -66,7 +66,7 @@ #include #endif -#if defined(CONFIG_COUNTER_RTC_STM32) +#if defined(CONFIG_COUNTER_RTC_STM32) || defined(CONFIG_RTC_STM32) #include #include #include diff --git a/tests/drivers/rtc/api/CMakeLists.txt b/tests/drivers/rtc/api/CMakeLists.txt new file mode 100644 index 000000000000..445aaf129642 --- /dev/null +++ b/tests/drivers/rtc/api/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(integration) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/drivers/rtc/api/prj.conf b/tests/drivers/rtc/api/prj.conf new file mode 100644 index 000000000000..509965a71ab9 --- /dev/null +++ b/tests/drivers/rtc/api/prj.conf @@ -0,0 +1,3 @@ +CONFIG_RTC=y +CONFIG_ZTEST=y +CONFIG_TEST_USERSPACE=y diff --git a/tests/drivers/rtc/api/src/main.c b/tests/drivers/rtc/api/src/main.c new file mode 100644 index 000000000000..c534ab35b3d5 --- /dev/null +++ b/tests/drivers/rtc/api/src/main.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 Paratronic + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(test); + +#define RTC_NAME DT_RTC_0_NAME + +void test_time(void) +{ + const int waitTime_ms = 1000; + int ret; + struct device *dev; + struct timespec ts0; + struct timespec ts; + s64_t delta; + + dev = device_get_binding(RTC_NAME); + zassert_not_null(dev, "Unable to get rtc device"); + + /* Set a particular time: 2020-03-23 12:22:40 */ + ts0.tv_sec = 1584966160; + ts0.tv_nsec = 0; + ret = rtc_set_time(dev, &ts0); + zassert_equal(ret, 0, "Fail to set realtime clock"); + + k_sleep(waitTime_ms); + + ret = rtc_get_time(dev, &ts); + zassert_equal(ret, 0, "Fail to get realtime clock"); + delta = (ts.tv_sec - ts0.tv_sec) * NSEC_PER_SEC + + (ts.tv_nsec - ts0.tv_nsec); + + delta = delta / (NSEC_PER_SEC / MSEC_PER_SEC); + + zassert_true((abs(waitTime_ms - delta) < 100), + "Clock inaccurate: %dms instead of %dms", + (int)delta, waitTime_ms); +} + +void test_main(void) +{ + struct device *dev; + + dev = device_get_binding(RTC_NAME); + zassert_not_null(dev, "Unable to get rtc device"); + k_object_access_grant(dev, k_current_get()); + ztest_test_suite(rtc_driver, + ztest_user_unit_test(test_time)); + ztest_run_test_suite(rtc_driver); +} diff --git a/tests/drivers/rtc/api/testcase.yaml b/tests/drivers/rtc/api/testcase.yaml new file mode 100644 index 000000000000..f22a440ffd8a --- /dev/null +++ b/tests/drivers/rtc/api/testcase.yaml @@ -0,0 +1,4 @@ +tests: + drivers.rtc: + depends_on: rtc + tags: drivers rtc