diff --git a/drivers/counter/Kconfig.gecko b/drivers/counter/Kconfig.gecko index 8a5fdafab4d..ded39e29685 100644 --- a/drivers/counter/Kconfig.gecko +++ b/drivers/counter/Kconfig.gecko @@ -17,8 +17,7 @@ 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 - select SOC_GECKO_PRS + select SOC_SILABS_SLEEPTIMER help Enable the counter driver for Sleep Timer module for Silicon Labs Gecko chips. diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index 653bff66a98..80f0d03e690 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -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_RV32M1_LPTMR_TIMER rv32m1_lptmr_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_TI_DM_TIMER ti_dmtimer.c) zephyr_library_sources_ifdef(CONFIG_XLNX_PSTTC_TIMER xlnx_psttc_timer.c) diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 8309fded781..4a30c7ddaae 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -91,6 +91,7 @@ source "drivers/timer/Kconfig.rcar_cmt" source "drivers/timer/Kconfig.riscv_machine" source "drivers/timer/Kconfig.rv32m1_lptmr" source "drivers/timer/Kconfig.sam0_rtc" +source "drivers/timer/Kconfig.silabs" source "drivers/timer/Kconfig.smartbond" source "drivers/timer/Kconfig.stm32_lptim" source "drivers/timer/Kconfig.ti_dm_timer" diff --git a/drivers/timer/Kconfig.silabs b/drivers/timer/Kconfig.silabs new file mode 100644 index 00000000000..2e9abba8ef6 --- /dev/null +++ b/drivers/timer/Kconfig.silabs @@ -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. diff --git a/drivers/timer/silabs_sleeptimer_timer.c b/drivers/timer/silabs_sleeptimer_timer.c new file mode 100644 index 00000000000..405fa1496a5 --- /dev/null +++ b/drivers/timer/silabs_sleeptimer_timer.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2024 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +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); diff --git a/modules/hal_silabs/simplicity_sdk/CMakeLists.txt b/modules/hal_silabs/simplicity_sdk/CMakeLists.txt index 949bf579447..0398ed5b75d 100644 --- a/modules/hal_silabs/simplicity_sdk/CMakeLists.txt +++ b/modules/hal_silabs/simplicity_sdk/CMakeLists.txt @@ -139,6 +139,19 @@ if(NOT SILABS_DEVICE_FAMILY_NUMBER EQUAL "21") ) 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_BURTC ${EMLIB_DIR}/src/em_burtc.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_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_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_TIMER ${EMLIB_DIR}/src/em_timer.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_USART ${EMLIB_DIR}/src/em_usart.c) diff --git a/soc/silabs/Kconfig b/soc/silabs/Kconfig index a9c9fde9da4..c9f7bdea180 100644 --- a/soc/silabs/Kconfig +++ b/soc/silabs/Kconfig @@ -121,6 +121,13 @@ config SOC_GECKO_TRNG help 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 config SOC_GECKO_PM_BACKEND_PMGR