From da0d9bab2df1e835b8ddbdba99472247a49e543a Mon Sep 17 00:00:00 2001 From: Krzysztof Chruscinski Date: Thu, 28 Jun 2018 09:30:04 +0200 Subject: [PATCH] drivers: counter: Counter API implementation for nRF Series (RTC). Shim for counter API using nrfx_rtc driver. Signed-off-by: Krzysztof Chruscinski --- drivers/counter/CMakeLists.txt | 1 + drivers/counter/counter_nrfx_rtc.c | 280 ++++++++++++++++++++++++++ drivers/timer/Kconfig | 1 + ext/hal/nordic/Kconfig | 12 ++ ext/hal/nordic/nrfx_config_nrf51.h | 12 ++ ext/hal/nordic/nrfx_config_nrf52832.h | 16 ++ ext/hal/nordic/nrfx_config_nrf52840.h | 16 ++ 7 files changed, 338 insertions(+) create mode 100644 drivers/counter/counter_nrfx_rtc.c diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index 26efb239578..eebe7f7107a 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -7,5 +7,6 @@ zephyr_library_sources_ifdef(CONFIG_TIMER_TMR_CMSDK_APB timer_tmr_cmsdk_apb.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_DTMR_CMSDK_APB counter_dtmr_cmsdk_apb.c) zephyr_library_sources_ifdef(CONFIG_TIMER_DTMR_CMSDK_APB timer_dtmr_cmsdk_apb.c) 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_USERSPACE counter_handlers.c) diff --git a/drivers/counter/counter_nrfx_rtc.c b/drivers/counter/counter_nrfx_rtc.c new file mode 100644 index 00000000000..6733801ad34 --- /dev/null +++ b/drivers/counter/counter_nrfx_rtc.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2018, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_COUNTER_LOG_LEVEL +#define LOG_MODULE_NAME counter_rtc +#include +LOG_MODULE_REGISTER(); + +#define RTC_CLOCK 32768 +#define COUNTER_MAX_WRAP RTC_COUNTER_COUNTER_Msk + +#define CC_TO_ID(cc) ((cc) - 1) +#define ID_TO_CC(id) ((id) + 1) + +#define WRAP_CH 0 +#define COUNTER_WRAP_INT NRFX_RTC_INT_COMPARE0 + +struct counter_nrfx_data { + counter_wrap_callback_t wrap_cb; + void *wrap_user_data; + u32_t wrap; +}; + +struct counter_nrfx_config { + struct counter_config_info info; + const struct counter_alarm_cfg **alarm_cfgs; + nrfx_rtc_t rtc; + + LOG_INSTANCE_PTR_DECLARE(log); +}; + +static inline struct counter_nrfx_data *get_dev_data(struct device *dev) +{ + return dev->driver_data; +} + +static inline const struct counter_nrfx_config *get_nrfx_config( + struct device *dev) +{ + return CONTAINER_OF(dev->config->config_info, + struct counter_nrfx_config, info); +} + +static int counter_nrfx_start(struct device *dev) +{ + nrfx_rtc_enable(&get_nrfx_config(dev)->rtc); + + return 0; +} + +static int counter_nrfx_stop(struct device *dev) +{ + nrfx_rtc_disable(&get_nrfx_config(dev)->rtc); + + return 0; +} + +static u32_t counter_nrfx_read(struct device *dev) +{ + return nrfx_rtc_counter_get(&get_nrfx_config(dev)->rtc); +} + +static int counter_nrfx_set_alarm(struct device *dev, + const struct counter_alarm_cfg *alarm_cfg) +{ + const struct counter_nrfx_config *nrfx_config = get_nrfx_config(dev); + const nrfx_rtc_t *rtc = &nrfx_config->rtc; + u32_t cc_val; + + if (alarm_cfg->ticks > get_dev_data(dev)->wrap) { + return -EINVAL; + } + + if (nrfx_config->alarm_cfgs[alarm_cfg->channel_id]) { + return -EBUSY; + } + + cc_val = alarm_cfg->ticks + (alarm_cfg->absolute ? + 0 : nrfx_rtc_counter_get(rtc)); + cc_val = (cc_val > get_dev_data(dev)->wrap) ? + (cc_val - get_dev_data(dev)->wrap) : cc_val; + nrfx_config->alarm_cfgs[alarm_cfg->channel_id] = alarm_cfg; + + nrfx_rtc_cc_set(rtc, ID_TO_CC(alarm_cfg->channel_id), cc_val, true); + + return 0; +} + +static void _disable(struct device *dev, u8_t id) +{ + const struct counter_nrfx_config *config = get_nrfx_config(dev); + + nrfx_rtc_cc_disable(&config->rtc, ID_TO_CC(id)); + config->alarm_cfgs[id] = NULL; +} + +static int counter_nrfx_disable_alarm(struct device *dev, + const struct counter_alarm_cfg *alarm_cfg) +{ + _disable(dev, alarm_cfg->channel_id); + + return 0; +} + +static int counter_nrfx_set_wrap(struct device *dev, u32_t ticks, + counter_wrap_callback_t callback, + void *user_data) +{ + const struct counter_nrfx_config *nrfx_config = get_nrfx_config(dev); + const nrfx_rtc_t *rtc = &nrfx_config->rtc; + struct counter_nrfx_data *dev_data = get_dev_data(dev); + + for (int i = 0; i < counter_get_num_of_channels(dev); i++) { + /* Overflow can be changed only when all alarms are + * disables. + */ + if (nrfx_config->alarm_cfgs[i]) { + return -EBUSY; + } + } + + nrfx_rtc_cc_disable(rtc, WRAP_CH); + nrfx_rtc_counter_clear(rtc); + + dev_data->wrap_cb = callback; + dev_data->wrap_user_data = user_data; + dev_data->wrap = ticks; + nrfx_rtc_cc_set(rtc, WRAP_CH, ticks, callback ? true : false); + + return 0; +} + +static u32_t counter_nrfx_get_pending_int(struct device *dev) +{ + return 0; +} + +static void alarm_event_handler(struct device *dev, u32_t id) +{ + const struct counter_nrfx_config *config = get_nrfx_config(dev); + const struct counter_alarm_cfg *alarm_cfg = config->alarm_cfgs[id]; + u32_t cc_val; + + cc_val = nrf_rtc_cc_get(config->rtc.p_reg, ID_TO_CC(id)); + + _disable(dev, id); + alarm_cfg->handler(dev, alarm_cfg, cc_val); +} + +static void event_handler(nrfx_rtc_int_type_t int_type, void *p_context) +{ + struct device *dev = p_context; + struct counter_nrfx_data *data = get_dev_data(dev); + + if (int_type == COUNTER_WRAP_INT) { + /* Manually reset counter when wrap is different than max wrap*/ + if (data->wrap != COUNTER_MAX_WRAP) { + nrfx_rtc_counter_clear(&get_nrfx_config(dev)->rtc); + nrfx_rtc_cc_set(&get_nrfx_config(dev)->rtc, + WRAP_CH, data->wrap, true); + } + + if (data->wrap_cb) { + data->wrap_cb(dev, data->wrap_user_data); + } + } else if (int_type > COUNTER_WRAP_INT) { + alarm_event_handler(dev, CC_TO_ID(int_type)); + + } +} + +static int init_rtc(struct device *dev, + const nrfx_rtc_config_t *config, + nrfx_rtc_handler_t handler) +{ + struct device *clock; + const struct counter_nrfx_config *nrfx_config = get_nrfx_config(dev); + const nrfx_rtc_t *rtc = &nrfx_config->rtc; + + clock = device_get_binding(CONFIG_CLOCK_CONTROL_NRF5_K32SRC_DRV_NAME); + if (!clock) { + return -ENODEV; + } + + clock_control_on(clock, (void *)CLOCK_CONTROL_NRF5_K32SRC); + + nrfx_err_t result = nrfx_rtc_init(rtc, config, handler); + + if (result != NRFX_SUCCESS) { + LOG_INST_ERR(nrfx_config->log, "Failed to initialize device."); + return -EBUSY; + } + + get_dev_data(dev)->wrap = COUNTER_MAX_WRAP; + + LOG_INST_DBG(nrfx_config->log, "Initialized"); + return 0; +} + +static u32_t counter_nrfx_get_wrap(struct device *dev) +{ + return get_dev_data(dev)->wrap; +} + +static u32_t counter_nrfx_get_max_relative_alarm(struct device *dev) +{ + /* Maybe decreased. */ + return get_dev_data(dev)->wrap; +} + +static const struct counter_driver_api counter_nrfx_driver_api = { + .start = counter_nrfx_start, + .stop = counter_nrfx_stop, + .read = counter_nrfx_read, + .set_alarm = counter_nrfx_set_alarm, + .disable_alarm = counter_nrfx_disable_alarm, + .set_wrap = counter_nrfx_set_wrap, + .get_pending_int = counter_nrfx_get_pending_int, + .get_wrap = counter_nrfx_get_wrap, + .get_max_relative_alarm = counter_nrfx_get_max_relative_alarm, +}; + +#define COUNTER_NRFX_RTC_DEVICE(idx) \ + DEVICE_DECLARE(rtc_##idx); \ + static void rtc_##idx##_handler(nrfx_rtc_int_type_t int_type) \ + { \ + event_handler(int_type, DEVICE_GET(rtc_##idx)); \ + } \ + static int counter_##idx##_init(struct device *dev) \ + { \ + IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_RTC##idx), \ + CONFIG_COUNTER_RTC##idx##_IRQ_PRI, \ + nrfx_isr, nrfx_rtc_##idx##_irq_handler, 0); \ + const nrfx_rtc_config_t config = { \ + .prescaler = CONFIG_COUNTER_RTC##idx##_PRESCALER, \ + }; \ + return init_rtc(dev, &config, rtc_##idx##_handler); \ + } \ + static struct counter_nrfx_data counter_##idx##_data; \ + static const struct counter_alarm_cfg \ + *counter##idx##_alarm_cfgs[CC_TO_ID(RTC##idx##_CC_NUM)]; \ + LOG_INSTANCE_REGISTER(LOG_MODULE_NAME, idx, CONFIG_COUNTER_LOG_LEVEL); \ + static const struct counter_nrfx_config nrfx_counter_##idx##_config = {\ + .info = { \ + .max_wrap = COUNTER_MAX_WRAP, \ + .freq = RTC_CLOCK / \ + (CONFIG_COUNTER_RTC##idx##_PRESCALER + 1), \ + .count_up = true, \ + .channels = CC_TO_ID(RTC##idx##_CC_NUM) \ + }, \ + .alarm_cfgs = counter##idx##_alarm_cfgs, \ + .rtc = NRFX_RTC_INSTANCE(idx), \ + LOG_INSTANCE_PTR_INIT(log, LOG_MODULE_NAME, idx) \ + }; \ + DEVICE_AND_API_INIT(rtc_##idx, CONFIG_COUNTER_RTC##idx##_NAME, \ + counter_##idx##_init, \ + &counter_##idx##_data, \ + &nrfx_counter_##idx##_config.info, \ + PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &counter_nrfx_driver_api) + +#ifdef CONFIG_COUNTER_RTC0 +COUNTER_NRFX_RTC_DEVICE(0); +#endif + +#ifdef CONFIG_COUNTER_RTC1 +COUNTER_NRFX_RTC_DEVICE(1); +#endif + +#ifdef CONFIG_COUNTER_RTC2 +COUNTER_NRFX_RTC_DEVICE(2); +#endif + diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 7987cc8b974..3f97a67d73c 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -113,6 +113,7 @@ config NRF_RTC_TIMER default y depends on CLOCK_CONTROL_NRF select TICKLESS_CAPABLE + select COUNTER_RTC1 help This module implements a kernel device driver for the nRF Real Time Counter NRF_RTC1 and provides the standard "system clock driver" diff --git a/ext/hal/nordic/Kconfig b/ext/hal/nordic/Kconfig index 4a7e9b9d4b5..e79f879c355 100644 --- a/ext/hal/nordic/Kconfig +++ b/ext/hal/nordic/Kconfig @@ -41,6 +41,18 @@ config NRFX_RTC config NRFX_SAADC bool +config NRFX_RTC + bool + +config NRFX_RTC0 + bool + +config NRFX_RTC1 + bool + +config NRFX_RTC2 + bool + config NRFX_SPI bool diff --git a/ext/hal/nordic/nrfx_config_nrf51.h b/ext/hal/nordic/nrfx_config_nrf51.h index d80fad097be..fba90c1ed64 100644 --- a/ext/hal/nordic/nrfx_config_nrf51.h +++ b/ext/hal/nordic/nrfx_config_nrf51.h @@ -781,6 +781,18 @@ // NRFX_RTC_ENABLED - nrfx_rtc - RTC peripheral driver //========================================================== +#ifdef CONFIG_NRFX_RTC +#define NRFX_RTC_ENABLED 1 +#endif + +#ifdef CONFIG_NRFX_RTC0 +#define NRFX_RTC0_ENABLED 1 +#endif + +#ifdef CONFIG_NRFX_RTC1 +#define NRFX_RTC1_ENABLED 1 +#endif + #ifndef NRFX_RTC_ENABLED #define NRFX_RTC_ENABLED 0 #endif diff --git a/ext/hal/nordic/nrfx_config_nrf52832.h b/ext/hal/nordic/nrfx_config_nrf52832.h index df48704fcc1..8e54babc59a 100644 --- a/ext/hal/nordic/nrfx_config_nrf52832.h +++ b/ext/hal/nordic/nrfx_config_nrf52832.h @@ -1466,6 +1466,22 @@ // NRFX_RTC_ENABLED - nrfx_rtc - RTC peripheral driver //========================================================== +#ifdef CONFIG_NRFX_RTC +#define NRFX_RTC_ENABLED 1 +#endif + +#ifdef CONFIG_NRFX_RTC0 +#define NRFX_RTC0_ENABLED 1 +#endif + +#ifdef CONFIG_NRFX_RTC1 +#define NRFX_RTC1_ENABLED 1 +#endif + +#ifdef CONFIG_NRFX_RTC2 +#define NRFX_RTC2_ENABLED 1 +#endif + #ifndef NRFX_RTC_ENABLED #define NRFX_RTC_ENABLED 0 #endif diff --git a/ext/hal/nordic/nrfx_config_nrf52840.h b/ext/hal/nordic/nrfx_config_nrf52840.h index c1abee13da1..3e2ca585ad8 100644 --- a/ext/hal/nordic/nrfx_config_nrf52840.h +++ b/ext/hal/nordic/nrfx_config_nrf52840.h @@ -1573,6 +1573,22 @@ // NRFX_RTC_ENABLED - nrfx_rtc - RTC peripheral driver //========================================================== +#ifdef CONFIG_NRFX_RTC +#define NRFX_RTC_ENABLED 1 +#endif + +#ifdef CONFIG_NRFX_RTC0 +#define NRFX_RTC0_ENABLED 1 +#endif + +#ifdef CONFIG_NRFX_RTC1 +#define NRFX_RTC1_ENABLED 1 +#endif + +#ifdef CONFIG_NRFX_RTC2 +#define NRFX_RTC2_ENABLED 1 +#endif + #ifndef NRFX_RTC_ENABLED #define NRFX_RTC_ENABLED 0 #endif