|  | /* | 
|  | * Copyright (c) 2021, Sateesh Kotapati | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT silabs_gecko_stimer | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <stddef.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/drivers/counter.h> | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/logging/log.h> | 
|  |  | 
|  | #include <em_cmu.h> | 
|  | #include <sl_atomic.h> | 
|  | #include <sl_sleeptimer.h> | 
|  | #include <sli_sleeptimer_hal.h> | 
|  |  | 
|  | LOG_MODULE_REGISTER(counter_gecko, CONFIG_COUNTER_LOG_LEVEL); | 
|  |  | 
|  | #if SL_SLEEPTIMER_PERIPHERAL == SL_SLEEPTIMER_PERIPHERAL_RTCC | 
|  | #define STIMER_IRQ_HANDLER RTCC_IRQHandler | 
|  | #define STIMER_MAX_VALUE _RTCC_CNT_MASK | 
|  | #elif SL_SLEEPTIMER_PERIPHERAL == SL_SLEEPTIMER_PERIPHERAL_SYSRTC | 
|  | #define STIMER_IRQ_HANDLER SYSRTC_APP_IRQHandler | 
|  | #define STIMER_MAX_VALUE _SYSRTC_CNT_MASK | 
|  | #else | 
|  | #error "Unsupported sleep timer peripheral" | 
|  | #endif | 
|  |  | 
|  | #define STIMER_ALARM_NUM 2 | 
|  |  | 
|  | struct counter_gecko_config { | 
|  | struct counter_config_info info; | 
|  | void (*irq_config)(void); | 
|  | uint32_t prescaler; | 
|  | }; | 
|  |  | 
|  | struct counter_gecko_alarm_data { | 
|  | counter_alarm_callback_t callback; | 
|  | uint8_t chan_id; | 
|  | uint32_t ticks; | 
|  | struct device *dev; | 
|  | void *user_data; | 
|  | }; | 
|  |  | 
|  | struct counter_gecko_top_data { | 
|  | counter_top_callback_t callback; | 
|  | uint32_t ticks; | 
|  | struct device *dev; | 
|  | void *user_data; | 
|  | }; | 
|  |  | 
|  | struct counter_gecko_data { | 
|  | struct counter_gecko_alarm_data alarm[STIMER_ALARM_NUM]; | 
|  | struct counter_gecko_top_data top_data; | 
|  | }; | 
|  |  | 
|  | static sl_sleeptimer_timer_handle_t alarm_timer[STIMER_ALARM_NUM]; | 
|  | static sl_sleeptimer_timer_handle_t top_timer; | 
|  |  | 
|  | #ifdef CONFIG_SOC_GECKO_HAS_ERRATA_RTCC_E201 | 
|  | #define ERRATA_RTCC_E201_MESSAGE                                                                   \ | 
|  | "Errata RTCC_E201: In case RTCC prescaler != 1 the module does not "                       \ | 
|  | "reset the counter value on CCV1 compare." | 
|  | #endif | 
|  |  | 
|  | static void alarm_callback(sl_sleeptimer_timer_handle_t *handle, void *data) | 
|  | { | 
|  | struct counter_gecko_alarm_data *alarm_data = (struct counter_gecko_alarm_data *)data; | 
|  | uint32_t count = | 
|  | ((sl_sleeptimer_get_tick_count()) % | 
|  | (((struct counter_gecko_data *const)(alarm_data->dev)->data)->top_data.ticks)); | 
|  |  | 
|  | if (alarm_data->callback != NULL) { | 
|  | alarm_data->callback( | 
|  | alarm_data->dev, alarm_data->chan_id, count, | 
|  | ((struct counter_alarm_cfg *)(alarm_data->user_data))->user_data); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void top_callback(sl_sleeptimer_timer_handle_t *handle, void *data) | 
|  | { | 
|  | struct counter_gecko_top_data *top_data = (struct counter_gecko_top_data *)data; | 
|  |  | 
|  | if (top_data->callback != NULL) { | 
|  | top_data->callback(top_data->dev, | 
|  | ((struct counter_top_cfg *)(top_data->user_data))->user_data); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int counter_gecko_get_value(const struct device *dev, uint32_t *ticks) | 
|  | { | 
|  | struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data; | 
|  |  | 
|  | *ticks = ((sl_sleeptimer_get_tick_count()) % (dev_data->top_data.ticks)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int counter_gecko_start(const struct device *dev) | 
|  | { | 
|  | ARG_UNUSED(dev); | 
|  |  | 
|  | sl_status_t error_code; | 
|  | bool is_top_timer_running = false; | 
|  |  | 
|  | error_code = sl_sleeptimer_is_timer_running(&top_timer, &is_top_timer_running); | 
|  | if ((error_code == SL_STATUS_OK) && (is_top_timer_running == true)) { | 
|  | return 0; | 
|  | } | 
|  | struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data; | 
|  |  | 
|  | error_code = sl_sleeptimer_start_timer(&top_timer, dev_data->top_data.ticks, top_callback, | 
|  | (void *)&dev_data->top_data, 0, 0); | 
|  | return error_code; | 
|  | } | 
|  |  | 
|  | static int counter_gecko_stop(const struct device *dev) | 
|  | { | 
|  | ARG_UNUSED(dev); | 
|  |  | 
|  | sl_status_t error_code; | 
|  | bool is_top_timer_running = false; | 
|  |  | 
|  | error_code = sl_sleeptimer_is_timer_running(&top_timer, &is_top_timer_running); | 
|  | if ((error_code == SL_STATUS_OK) && (is_top_timer_running == true)) { | 
|  | sl_sleeptimer_stop_timer(&top_timer); | 
|  | } | 
|  | return error_code; | 
|  | } | 
|  |  | 
|  | static int counter_gecko_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg) | 
|  | { | 
|  | struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data; | 
|  | sl_status_t error_code; | 
|  | bool is_top_timer_running = false; | 
|  |  | 
|  | #ifdef CONFIG_SOC_GECKO_HAS_ERRATA_RTCC_E201 | 
|  | const struct counter_gecko_config *const dev_cfg = | 
|  | (const struct counter_gecko_config *const)(dev)->config; | 
|  |  | 
|  | if (dev_cfg->prescaler != 1) { | 
|  | LOG_ERR(ERRATA_RTCC_E201_MESSAGE); | 
|  | return -EINVAL; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | error_code = sl_sleeptimer_is_timer_running(&top_timer, &is_top_timer_running); | 
|  | if ((error_code == SL_STATUS_OK) && (is_top_timer_running == true)) { | 
|  | sl_sleeptimer_stop_timer(&top_timer); | 
|  | } | 
|  |  | 
|  | dev_data->top_data.callback = cfg->callback; | 
|  | dev_data->top_data.ticks = cfg->ticks; | 
|  | dev_data->top_data.dev = (struct device *)dev; | 
|  | dev_data->top_data.user_data = (struct counter_top_cfg *)cfg; | 
|  |  | 
|  | error_code = sl_sleeptimer_start_periodic_timer(&top_timer, cfg->ticks, top_callback, | 
|  | (void *)&dev_data->top_data, 0, cfg->flags); | 
|  |  | 
|  | return error_code; | 
|  | } | 
|  |  | 
|  | static uint32_t counter_gecko_get_top_value(const struct device *dev) | 
|  | { | 
|  | struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data; | 
|  |  | 
|  | return dev_data->top_data.ticks; | 
|  | } | 
|  |  | 
|  | static int counter_gecko_set_alarm(const struct device *dev, uint8_t chan_id, | 
|  | const struct counter_alarm_cfg *alarm_cfg) | 
|  | { | 
|  | bool is_alarm_timer_running = false; | 
|  | struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data; | 
|  | sl_status_t error_code; | 
|  | uint32_t now_ticks = 0; | 
|  | uint32_t top_val = counter_gecko_get_top_value(dev); | 
|  |  | 
|  | if ((top_val != 0) && (alarm_cfg->ticks > top_val)) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (chan_id >= STIMER_ALARM_NUM) { | 
|  | printk("Alarm timer count exceeded\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | error_code = sl_sleeptimer_is_timer_running(&alarm_timer[chan_id], &is_alarm_timer_running); | 
|  | if ((error_code == SL_STATUS_OK) && (is_alarm_timer_running == true)) { | 
|  | sl_sleeptimer_stop_timer(&alarm_timer[chan_id]); | 
|  | } | 
|  |  | 
|  | if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) != 0) { | 
|  | /* Absolute */ | 
|  | error_code = counter_gecko_get_value(dev, &now_ticks); | 
|  | if (now_ticks < alarm_cfg->ticks) { | 
|  | dev_data->alarm[chan_id].ticks = top_val + (alarm_cfg->ticks - now_ticks); | 
|  | } else { | 
|  | dev_data->alarm[chan_id].ticks = | 
|  | (top_val - (now_ticks - alarm_cfg->ticks)) % top_val; | 
|  | } | 
|  |  | 
|  | } else { | 
|  | /* Relative */ | 
|  | dev_data->alarm[chan_id].ticks = alarm_cfg->ticks; | 
|  | } | 
|  |  | 
|  | dev_data->alarm[chan_id].callback = alarm_cfg->callback; | 
|  | dev_data->alarm[chan_id].chan_id = chan_id; | 
|  | dev_data->alarm[chan_id].dev = (struct device *)dev; | 
|  | dev_data->alarm[chan_id].user_data = (struct counter_alarm_cfg *)alarm_cfg; | 
|  |  | 
|  | error_code = | 
|  | sl_sleeptimer_start_timer(&alarm_timer[chan_id], dev_data->alarm[chan_id].ticks, | 
|  | alarm_callback, (void *)&dev_data->alarm[chan_id], 0, 0); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int counter_gecko_cancel_alarm(const struct device *dev, uint8_t chan_id) | 
|  | { | 
|  | struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data; | 
|  |  | 
|  | if (chan_id >= STIMER_ALARM_NUM) { | 
|  | LOG_DBG("Alarm timer count exceeded\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | sl_sleeptimer_stop_timer(&alarm_timer[chan_id]); | 
|  |  | 
|  | dev_data->alarm[chan_id].callback = NULL; | 
|  | dev_data->alarm[chan_id].user_data = NULL; | 
|  |  | 
|  | LOG_DBG("cancel alarm: channel %u", chan_id); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static uint32_t counter_gecko_get_pending_int(const struct device *dev) | 
|  | { | 
|  | ARG_UNUSED(dev); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int counter_gecko_init(const struct device *dev) | 
|  | { | 
|  | const struct counter_gecko_config *const dev_cfg = | 
|  | (const struct counter_gecko_config *const)(dev)->config; | 
|  | struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data; | 
|  |  | 
|  | sl_sleeptimer_init(); | 
|  | dev_data->top_data.ticks = STIMER_MAX_VALUE; | 
|  |  | 
|  | /* Configure & enable module interrupts */ | 
|  | dev_cfg->irq_config(); | 
|  |  | 
|  | LOG_INF("Device %s initialized", (dev)->name); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static DEVICE_API(counter, counter_gecko_driver_api) = { | 
|  | .start = counter_gecko_start, | 
|  | .stop = counter_gecko_stop, | 
|  | .get_value = counter_gecko_get_value, | 
|  | .set_alarm = counter_gecko_set_alarm, | 
|  | .cancel_alarm = counter_gecko_cancel_alarm, | 
|  | .set_top_value = counter_gecko_set_top_value, | 
|  | .get_pending_int = counter_gecko_get_pending_int, | 
|  | .get_top_value = counter_gecko_get_top_value, | 
|  | }; | 
|  |  | 
|  | BUILD_ASSERT((DT_INST_PROP(0, prescaler) > 0U) && (DT_INST_PROP(0, prescaler) <= 32768U)); | 
|  |  | 
|  | static void counter_gecko_0_irq_config(void) | 
|  | { | 
|  | IRQ_DIRECT_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), STIMER_IRQ_HANDLER, 0); | 
|  | irq_enable(DT_INST_IRQN(0)); | 
|  | } | 
|  |  | 
|  | static const struct counter_gecko_config counter_gecko_0_config = { | 
|  | .info = { | 
|  | .max_top_value = STIMER_MAX_VALUE, | 
|  | .freq = DT_INST_PROP(0, clock_frequency) / DT_INST_PROP(0, prescaler), | 
|  | .flags = COUNTER_CONFIG_INFO_COUNT_UP, | 
|  | .channels = STIMER_ALARM_NUM, | 
|  | }, | 
|  | .irq_config = counter_gecko_0_irq_config, | 
|  | .prescaler = DT_INST_PROP(0, prescaler), | 
|  | }; | 
|  |  | 
|  | static struct counter_gecko_data counter_gecko_0_data; | 
|  |  | 
|  | DEVICE_DT_INST_DEFINE(0, counter_gecko_init, NULL, &counter_gecko_0_data, &counter_gecko_0_config, | 
|  | PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &counter_gecko_driver_api); |