drivers: timer: silabs: Add sleeptimer timer driver

Add OS timer implementation making use of the Sleeptimer HAL.
Sleeptimer integrates tightly with the Silabs Power Manager HAL,
and must be used as the OS timer to achieve optimal power consumption
when using the radio.

Signed-off-by: Aksel Skauge Mellbye <aksel.mellbye@silabs.com>
This commit is contained in:
Aksel Skauge Mellbye 2024-10-21 12:27:03 +02:00 committed by Carles Cufí
commit da6ddc92cd
7 changed files with 202 additions and 8 deletions

View file

@ -17,8 +17,7 @@ config COUNTER_GECKO_STIMER
bool "Silicon Labs Gecko Counter Sleep Timer driver" bool "Silicon Labs Gecko Counter Sleep Timer driver"
default y default y
depends on DT_HAS_SILABS_GECKO_STIMER_ENABLED depends on DT_HAS_SILABS_GECKO_STIMER_ENABLED
select SOC_GECKO_RTCC select SOC_SILABS_SLEEPTIMER
select SOC_GECKO_PRS
help help
Enable the counter driver for Sleep Timer module for Silicon Labs Enable the counter driver for Sleep Timer module for Silicon Labs
Gecko chips. Gecko chips.

View file

@ -32,6 +32,7 @@ zephyr_library_sources_ifdef(CONFIG_RCAR_CMT_TIMER rcar_cmt_timer.c)
zephyr_library_sources_ifdef(CONFIG_RISCV_MACHINE_TIMER riscv_machine_timer.c) zephyr_library_sources_ifdef(CONFIG_RISCV_MACHINE_TIMER riscv_machine_timer.c)
zephyr_library_sources_ifdef(CONFIG_RV32M1_LPTMR_TIMER rv32m1_lptmr_timer.c) zephyr_library_sources_ifdef(CONFIG_RV32M1_LPTMR_TIMER rv32m1_lptmr_timer.c)
zephyr_library_sources_ifdef(CONFIG_SAM0_RTC_TIMER sam0_rtc_timer.c) zephyr_library_sources_ifdef(CONFIG_SAM0_RTC_TIMER sam0_rtc_timer.c)
zephyr_library_sources_ifdef(CONFIG_SILABS_SLEEPTIMER_TIMER silabs_sleeptimer_timer.c)
zephyr_library_sources_ifdef(CONFIG_STM32_LPTIM_TIMER stm32_lptim_timer.c) zephyr_library_sources_ifdef(CONFIG_STM32_LPTIM_TIMER stm32_lptim_timer.c)
zephyr_library_sources_ifdef(CONFIG_TI_DM_TIMER ti_dmtimer.c) zephyr_library_sources_ifdef(CONFIG_TI_DM_TIMER ti_dmtimer.c)
zephyr_library_sources_ifdef(CONFIG_XLNX_PSTTC_TIMER xlnx_psttc_timer.c) zephyr_library_sources_ifdef(CONFIG_XLNX_PSTTC_TIMER xlnx_psttc_timer.c)

View file

@ -91,6 +91,7 @@ source "drivers/timer/Kconfig.rcar_cmt"
source "drivers/timer/Kconfig.riscv_machine" source "drivers/timer/Kconfig.riscv_machine"
source "drivers/timer/Kconfig.rv32m1_lptmr" source "drivers/timer/Kconfig.rv32m1_lptmr"
source "drivers/timer/Kconfig.sam0_rtc" source "drivers/timer/Kconfig.sam0_rtc"
source "drivers/timer/Kconfig.silabs"
source "drivers/timer/Kconfig.smartbond" source "drivers/timer/Kconfig.smartbond"
source "drivers/timer/Kconfig.stm32_lptim" source "drivers/timer/Kconfig.stm32_lptim"
source "drivers/timer/Kconfig.ti_dm_timer" source "drivers/timer/Kconfig.ti_dm_timer"

View file

@ -0,0 +1,13 @@
# Copyright (c) 2024 Silicon Laboratories Inc.
# SPDX-License-Identifier: Apache-2.0
config SILABS_SLEEPTIMER_TIMER
bool "Silabs Sleeptimer system clock driver"
depends on SOC_FAMILY_SILABS_S2
depends on DT_HAS_SILABS_GECKO_STIMER_ENABLED
select SOC_SILABS_SLEEPTIMER
select TICKLESS_CAPABLE
select TIMER_READS_ITS_FREQUENCY_AT_RUNTIME
help
This module implements a kernel device driver for the sleeptimer
and provides the standard "system clock driver" interfaces.

View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2024 Silicon Laboratories Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <stdint.h>
#include <stdbool.h>
#include <zephyr/init.h>
#include <zephyr/device.h>
#include <zephyr/kernel.h>
#include <zephyr/sys_clock.h>
#include <zephyr/drivers/timer/system_timer.h>
#include <zephyr/logging/log.h>
#include <sl_sleeptimer.h>
LOG_MODULE_REGISTER(silabs_sleeptimer_timer);
/* Maximum time interval between timer interrupts (in hw_cycles) */
#define MAX_TIMEOUT_CYC (UINT32_MAX >> 1)
#define MIN_DELAY_CYC (4U)
#define DT_RTC DT_COMPAT_GET_ANY_STATUS_OKAY(silabs_gecko_stimer)
/* Ensure interrupt names don't expand to register interface struct pointers */
#undef RTCC
/* With CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME, this global variable holds the clock frequency,
* and must be written by the driver at init.
*/
extern int z_clock_hw_cycles_per_sec;
/* Global timer state */
struct sleeptimer_timer_data {
uint32_t cyc_per_tick; /* Number of hw_cycles per 1 kernel tick */
uint32_t max_timeout_ticks; /* MAX_TIMEOUT_CYC expressed as ticks */
atomic_t last_count; /* Value of counter when the previous tick was announced */
struct k_spinlock lock; /* Spinlock to sync between ISR and updating the timeout */
bool initialized; /* Set to true when timer is initialized */
sl_sleeptimer_timer_handle_t handle; /* Timer handle for system timer */
};
static struct sleeptimer_timer_data g_sleeptimer_timer_data = {0};
static void sleeptimer_cb(sl_sleeptimer_timer_handle_t *handle, void *data)
{
ARG_UNUSED(handle);
struct sleeptimer_timer_data *timer = data;
uint32_t curr = sl_sleeptimer_get_tick_count();
uint32_t prev = atomic_get(&timer->last_count);
uint32_t pending = curr - prev;
/* Number of unannounced ticks since the last announcement */
uint32_t unannounced = pending / timer->cyc_per_tick;
atomic_set(&timer->last_count, prev + unannounced * timer->cyc_per_tick);
sys_clock_announce(unannounced);
}
static void sleeptimer_clock_set_timeout(int32_t ticks, struct sleeptimer_timer_data *timer)
{
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
return;
}
ticks = (ticks == K_TICKS_FOREVER) ? timer->max_timeout_ticks : ticks;
ticks = CLAMP(ticks, 0, timer->max_timeout_ticks);
k_spinlock_key_t key = k_spin_lock(&timer->lock);
uint32_t curr = sl_sleeptimer_get_tick_count();
uint32_t prev = atomic_get(&timer->last_count);
uint32_t pending = curr - prev;
uint32_t next = ticks * timer->cyc_per_tick;
/* Next timeout is N ticks in the future, minus the current progress
* towards the timeout. If we are behind, set the timeout to the first
* possible upcoming tick.
*/
while (next < (pending + MIN_DELAY_CYC)) {
next += timer->cyc_per_tick;
}
next -= pending;
sl_sleeptimer_restart_timer(&timer->handle, next, sleeptimer_cb, timer, 0, 0);
k_spin_unlock(&timer->lock, key);
}
static uint32_t sleeptimer_clock_elapsed(struct sleeptimer_timer_data *timer)
{
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL) || !timer->initialized) {
/* No unannounced ticks can have elapsed if not in tickless mode */
return 0;
} else {
return (sl_sleeptimer_get_tick_count() - atomic_get(&timer->last_count)) /
timer->cyc_per_tick;
}
}
void sys_clock_set_timeout(int32_t ticks, bool idle)
{
ARG_UNUSED(idle);
sleeptimer_clock_set_timeout(ticks, &g_sleeptimer_timer_data);
}
uint32_t sys_clock_elapsed(void)
{
return sleeptimer_clock_elapsed(&g_sleeptimer_timer_data);
}
uint32_t sys_clock_cycle_get_32(void)
{
return g_sleeptimer_timer_data.initialized ? sl_sleeptimer_get_tick_count() : 0;
}
static int sleeptimer_init(void)
{
sl_status_t status = SL_STATUS_OK;
struct sleeptimer_timer_data *timer = &g_sleeptimer_timer_data;
IRQ_CONNECT(DT_IRQ(DT_RTC, irq), DT_IRQ(DT_RTC, priority),
CONCAT(DT_STRING_UPPER_TOKEN_BY_IDX(DT_RTC, interrupt_names, 0), _IRQHandler),
0, 0);
sl_sleeptimer_init();
z_clock_hw_cycles_per_sec = sl_sleeptimer_get_timer_frequency();
BUILD_ASSERT(CONFIG_SYS_CLOCK_TICKS_PER_SEC > 0,
"Invalid CONFIG_SYS_CLOCK_TICKS_PER_SEC value");
timer->cyc_per_tick = z_clock_hw_cycles_per_sec / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
__ASSERT(timer->cyc_per_tick >= MIN_DELAY_CYC,
"A tick of %u cycles is too short to be scheduled "
"(min is %u). Config: SYS_CLOCK_TICKS_PER_SEC is "
"%d and timer frequency is %u",
timer->cyc_per_tick, MIN_DELAY_CYC, CONFIG_SYS_CLOCK_TICKS_PER_SEC,
z_clock_hw_cycles_per_sec);
timer->max_timeout_ticks = MAX_TIMEOUT_CYC / timer->cyc_per_tick;
timer->initialized = true;
atomic_set(&timer->last_count, sl_sleeptimer_get_tick_count());
/* Start the timer and announce 1 kernel tick */
if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
status = sl_sleeptimer_start_timer(&timer->handle, timer->cyc_per_tick,
sleeptimer_cb, timer, 0, 0);
} else {
status = sl_sleeptimer_start_periodic_timer(&timer->handle, timer->cyc_per_tick,
sleeptimer_cb, timer, 0, 0);
}
if (status != SL_STATUS_OK) {
return -ENODEV;
}
return 0;
}
SYS_INIT(sleeptimer_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);

View file

@ -139,6 +139,19 @@ if(NOT SILABS_DEVICE_FAMILY_NUMBER EQUAL "21")
) )
endif() endif()
# Sleeptimer
if(CONFIG_SOC_SILABS_SLEEPTIMER)
zephyr_library_sources(
${PERIPHERAL_DIR}/src/sl_hal_sysrtc.c
${SERVICE_DIR}/sleeptimer/src/sl_sleeptimer_hal_rtcc.c
${SERVICE_DIR}/sleeptimer/src/sl_sleeptimer_hal_sysrtc.c
${SERVICE_DIR}/sleeptimer/src/sl_sleeptimer.c
)
zephyr_compile_definitions(
SL_CATALOG_SLEEPTIMER_PRESENT
)
endif()
zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_IADC ${EMLIB_DIR}/src/em_iadc.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_IADC ${EMLIB_DIR}/src/em_iadc.c)
zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_BURTC ${EMLIB_DIR}/src/em_burtc.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_BURTC ${EMLIB_DIR}/src/em_burtc.c)
zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_CMU ${EMLIB_DIR}/src/em_cmu.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_CMU ${EMLIB_DIR}/src/em_cmu.c)
@ -172,12 +185,6 @@ zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_PRS ${EMLIB_DIR}/src/em_p
zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_RMU ${EMLIB_DIR}/src/em_rmu.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_RMU ${EMLIB_DIR}/src/em_rmu.c)
zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_RTC ${EMLIB_DIR}/src/em_rtc.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_RTC ${EMLIB_DIR}/src/em_rtc.c)
zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_RTCC ${EMLIB_DIR}/src/em_rtcc.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_RTCC ${EMLIB_DIR}/src/em_rtcc.c)
zephyr_library_sources_ifdef(CONFIG_COUNTER_GECKO_STIMER
${PERIPHERAL_DIR}/src/sl_hal_sysrtc.c
${SERVICE_DIR}/sleeptimer/src/sl_sleeptimer_hal_rtcc.c
${SERVICE_DIR}/sleeptimer/src/sl_sleeptimer_hal_sysrtc.c
${SERVICE_DIR}/sleeptimer/src/sl_sleeptimer.c
)
zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_EUSART ${EMLIB_DIR}/src/em_eusart.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_EUSART ${EMLIB_DIR}/src/em_eusart.c)
zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_TIMER ${EMLIB_DIR}/src/em_timer.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_TIMER ${EMLIB_DIR}/src/em_timer.c)
zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_USART ${EMLIB_DIR}/src/em_usart.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_USART ${EMLIB_DIR}/src/em_usart.c)

View file

@ -121,6 +121,13 @@ config SOC_GECKO_TRNG
help help
Set if the SoC has a True Random Number Generator (TRNG) module. Set if the SoC has a True Random Number Generator (TRNG) module.
config SOC_SILABS_SLEEPTIMER
bool
select SOC_GECKO_PRS
select SOC_GECKO_RTCC if SOC_SERIES_EFR32BG22 || SOC_SERIES_EFR32BG27 || SOC_SERIES_EFR32MG21
help
Set if the Sleeptimer HAL module is used.
if PM if PM
config SOC_GECKO_PM_BACKEND_PMGR config SOC_GECKO_PM_BACKEND_PMGR