drivers: counter: Add counter_gecko_stimer driver
This commit adds initial support for Silabs Real-Time counter Co-authored-by: Filip Kokosinski <fkokosinski@antmicro.com> Signed-off-by: Mateusz Sierszulski <msierszulski@antmicro.com>
This commit is contained in:
parent
9756766892
commit
d4a6a14926
4 changed files with 313 additions and 0 deletions
|
@ -5,6 +5,7 @@ zephyr_library()
|
|||
zephyr_library_sources_ifdef(CONFIG_TIMER_TMR_CMSDK_APB timer_tmr_cmsdk_apb.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_TIMER_DTMR_CMSDK_APB timer_dtmr_cmsdk_apb.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_COUNTER_GECKO_RTCC counter_gecko_rtcc.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_COUNTER_GECKO_STIMER counter_gecko_stimer.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_COUNTER_IMX_EPIT counter_imx_epit.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_CTIMER counter_mcux_ctimer.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_RTC counter_mcux_rtc.c)
|
||||
|
|
|
@ -12,3 +12,12 @@ config COUNTER_GECKO_RTCC
|
|||
help
|
||||
Enable counter driver based on RTCC module for Silicon Labs Gecko
|
||||
chips.
|
||||
|
||||
config COUNTER_GECKO_STIMER
|
||||
bool "Silicon Labs Gecko Counter Sleep Timer driver"
|
||||
default y
|
||||
depends on DT_HAS_SILABS_GECKO_STIMER_ENABLED
|
||||
select SOC_GECKO_RTCC
|
||||
help
|
||||
Enable the counter driver for Sleep Timer module for Silicon Labs
|
||||
Gecko chips.
|
||||
|
|
291
drivers/counter/counter_gecko_stimer.c
Normal file
291
drivers/counter/counter_gecko_stimer.c
Normal file
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
LOG_MODULE_REGISTER(counter_gecko, CONFIG_COUNTER_LOG_LEVEL);
|
||||
|
||||
#define STIMER_MAX_VALUE (_RTCC_CNT_MASK)
|
||||
#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 const struct counter_driver_api 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), RTCC_IRQHandler, 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);
|
12
dts/bindings/rtc/silabs,gecko-stimer.yaml
Normal file
12
dts/bindings/rtc/silabs,gecko-stimer.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Copyright (c) 2021, Sateesh Kotapati
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Silabs Gecko Sleep Timer (Real-Time Counter)
|
||||
|
||||
compatible: "silabs,gecko-stimer"
|
||||
|
||||
include: rtc.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
Loading…
Add table
Add a link
Reference in a new issue