/* * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #define LOG_LEVEL CONFIG_COUNTER_LOG_LEVEL #define LOG_MODULE_NAME counter_timer #include LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL); #define TIMER_CLOCK 16000000 #define CC_TO_ID(cc_num) (cc_num - 2) #define ID_TO_CC(idx) (nrf_timer_cc_channel_t)(idx + 2) #define COUNTER_EVENT_TO_ID(evt) \ (evt - NRF_TIMER_EVENT_COMPARE2)/sizeof(u32_t) #define TOP_CH NRF_TIMER_CC_CHANNEL0 #define COUNTER_TOP_INT NRF_TIMER_EVENT_COMPARE0 #define COUNTER_OVERFLOW_SHORT NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK #define COUNTER_READ_CC NRF_TIMER_CC_CHANNEL1 struct counter_nrfx_data { counter_top_callback_t top_cb; void *top_user_data; }; struct counter_nrfx_ch_data { counter_alarm_callback_t callback; void *user_data; }; struct counter_nrfx_config { struct counter_config_info info; struct counter_nrfx_ch_data *ch_data; nrfx_timer_t timer; 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_timer_enable(&get_nrfx_config(dev)->timer); return 0; } static int counter_nrfx_stop(struct device *dev) { nrfx_timer_disable(&get_nrfx_config(dev)->timer); return 0; } static u32_t counter_nrfx_read(struct device *dev) { return nrfx_timer_capture(&get_nrfx_config(dev)->timer, COUNTER_READ_CC); } static int counter_nrfx_set_alarm(struct device *dev, u8_t chan_id, const struct counter_alarm_cfg *alarm_cfg) { const struct counter_nrfx_config *nrfx_config = get_nrfx_config(dev); const nrfx_timer_t *timer = &nrfx_config->timer; u32_t cc_val; if (alarm_cfg->ticks > nrfx_timer_capture_get(timer, TOP_CH)) { return -EINVAL; } if (nrfx_config->ch_data[chan_id].callback) { return -EBUSY; } cc_val = alarm_cfg->ticks + (alarm_cfg->absolute ? 0 : nrfx_timer_capture(timer, COUNTER_READ_CC)); nrfx_config->ch_data[chan_id].callback = alarm_cfg->callback; nrfx_config->ch_data[chan_id].user_data = alarm_cfg->user_data; nrfx_timer_compare(timer, ID_TO_CC(chan_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_timer_compare_int_disable(&config->timer, ID_TO_CC(id)); config->ch_data[id].callback = NULL; } static int counter_nrfx_disable_alarm(struct device *dev, u8_t chan_id) { _disable(dev, chan_id); return 0; } static int counter_nrfx_set_top_value(struct device *dev, u32_t ticks, counter_top_callback_t callback, void *user_data) { const struct counter_nrfx_config *nrfx_config = get_nrfx_config(dev); const nrfx_timer_t *timer = &nrfx_config->timer; struct counter_nrfx_data *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->ch_data[i].callback) { return -EBUSY; } } nrfx_timer_compare_int_disable(timer, TOP_CH); nrfx_timer_clear(timer); data->top_cb = callback; data->top_user_data = user_data; nrfx_timer_extended_compare(timer, TOP_CH, ticks, COUNTER_OVERFLOW_SHORT, 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); counter_alarm_callback_t clbk = config->ch_data[id].callback; u32_t cc_val; if (!clbk) { return; } cc_val = nrfx_timer_capture_get(&config->timer, ID_TO_CC(id)); _disable(dev, id); clbk(dev, id, cc_val, config->ch_data[id].user_data); } static void event_handler(nrf_timer_event_t event_type, void *p_context) { struct device *dev = p_context; struct counter_nrfx_data *dev_data = get_dev_data(dev); if (event_type == COUNTER_TOP_INT) { if (dev_data->top_cb) { dev_data->top_cb(dev, dev_data->top_user_data); } } else if (event_type > NRF_TIMER_EVENT_COMPARE1) { alarm_event_handler(dev, COUNTER_EVENT_TO_ID(event_type)); } } static int init_timer(struct device *dev, const nrfx_timer_config_t *config) { const struct counter_nrfx_config *nrfx_config = get_nrfx_config(dev); const nrfx_timer_t *timer = &nrfx_config->timer; nrfx_err_t result = nrfx_timer_init(timer, config, event_handler); if (result != NRFX_SUCCESS) { LOG_INST_ERR(nrfx_config->log, "Failed to initialize device."); return -EBUSY; } nrfx_timer_compare(timer, TOP_CH, UINT32_MAX, false); LOG_INST_DBG(nrfx_config->log, "Initialized"); return 0; } static u32_t counter_nrfx_get_top_value(struct device *dev) { return nrfx_timer_capture_get(&get_nrfx_config(dev)->timer, TOP_CH); } static u32_t counter_nrfx_get_max_relative_alarm(struct device *dev) { return nrfx_timer_capture_get(&get_nrfx_config(dev)->timer, TOP_CH); } 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_top_value = counter_nrfx_set_top_value, .get_pending_int = counter_nrfx_get_pending_int, .get_top_value = counter_nrfx_get_top_value, .get_max_relative_alarm = counter_nrfx_get_max_relative_alarm, }; #define COUNTER_NRFX_TIMER_DEVICE(idx) \ static int counter_##idx##_init(struct device *dev) \ { \ IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_TIMER##idx), \ CONFIG_COUNTER_TIMER##idx##_IRQ_PRI, \ nrfx_isr, nrfx_timer_##idx##_irq_handler, 0); \ const nrfx_timer_config_t config = { \ .frequency = CONFIG_COUNTER_TIMER##idx##_PRESCALER, \ .mode = NRF_TIMER_MODE_TIMER, \ .bit_width = (TIMER##idx##_MAX_SIZE == 32) ? \ NRF_TIMER_BIT_WIDTH_32 : \ NRF_TIMER_BIT_WIDTH_16, \ .p_context = dev \ }; \ return init_timer(dev, &config); \ } \ static struct counter_nrfx_data counter_##idx##_data; \ static struct counter_nrfx_ch_data \ counter##idx##_ch_data[CC_TO_ID(TIMER##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_top_value = (TIMER##idx##_MAX_SIZE == 32) ? \ 0xffffffff : 0x0000ffff, \ .freq = TIMER_CLOCK / \ (1 << CONFIG_COUNTER_TIMER##idx##_PRESCALER), \ .count_up = true, \ .channels = CC_TO_ID(TIMER##idx##_CC_NUM), \ }, \ .ch_data = counter##idx##_ch_data, \ .timer = NRFX_TIMER_INSTANCE(idx), \ LOG_INSTANCE_PTR_INIT(log, LOG_MODULE_NAME, idx) \ }; \ DEVICE_AND_API_INIT(timer_##idx, CONFIG_COUNTER_TIMER##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_TIMER0 COUNTER_NRFX_TIMER_DEVICE(0); #endif #ifdef CONFIG_COUNTER_TIMER1 COUNTER_NRFX_TIMER_DEVICE(1); #endif #ifdef CONFIG_COUNTER_TIMER2 COUNTER_NRFX_TIMER_DEVICE(2); #endif #ifdef CONFIG_COUNTER_TIMER3 COUNTER_NRFX_TIMER_DEVICE(3); #endif #ifdef CONFIG_COUNTER_TIMER4 COUNTER_NRFX_TIMER_DEVICE(4); #endif