From 7dc6fb442f1f0458b49cb928278b3fe8d6af8495 Mon Sep 17 00:00:00 2001 From: Aksel Skauge Mellbye Date: Wed, 12 Mar 2025 20:13:59 +0100 Subject: [PATCH] drivers: timer: silabs: Fix calculation of next tick In the case where more than a full tick was unannounced when sys_clock_set_timeout() was called, the timer driver would subtract it from the next timeout. However, this is already done by the caller through the elapsed() function in timeout.c, leading to the timer interrupt firing too early. With this fix, SYS_CLOCK_TICKS_PER_SEC can be increased to the full speed of the low frequency timer. The underlying sleeptimer API must be called with a timeout of at least 1, and will if needed increase the value to the minimum value required by the hardware. Signed-off-by: Aksel Skauge Mellbye --- drivers/timer/silabs_sleeptimer_timer.c | 22 +++++++--------------- soc/silabs/silabs_s2/Kconfig.defconfig | 4 +++- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/timer/silabs_sleeptimer_timer.c b/drivers/timer/silabs_sleeptimer_timer.c index 405fa1496a5..cc9533e97db 100644 --- a/drivers/timer/silabs_sleeptimer_timer.c +++ b/drivers/timer/silabs_sleeptimer_timer.c @@ -21,7 +21,6 @@ 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) @@ -73,16 +72,15 @@ static void sleeptimer_clock_set_timeout(int32_t ticks, struct sleeptimer_timer_ 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 pending = curr % timer->cyc_per_tick; 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. + * into the tick if using multiple cycles per tick. The HAL API must + * be called with a timeout of at least one cycle. */ - while (next < (pending + MIN_DELAY_CYC)) { - next += timer->cyc_per_tick; + if (next == 0) { + next = timer->cyc_per_tick; } next -= pending; @@ -136,17 +134,11 @@ static int sleeptimer_init(void) 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()); + atomic_set(&timer->last_count, + ROUND_DOWN(sl_sleeptimer_get_tick_count(), timer->cyc_per_tick)); /* Start the timer and announce 1 kernel tick */ if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { diff --git a/soc/silabs/silabs_s2/Kconfig.defconfig b/soc/silabs/silabs_s2/Kconfig.defconfig index 9f3b69f593a..339b1df6778 100644 --- a/soc/silabs/silabs_s2/Kconfig.defconfig +++ b/soc/silabs/silabs_s2/Kconfig.defconfig @@ -10,7 +10,9 @@ configdefault SYS_CLOCK_HW_CYCLES_PER_SEC default 32768 configdefault SYS_CLOCK_TICKS_PER_SEC - default 1024 if SILABS_SLEEPTIMER_TIMER || GECKO_BURTC_TIMER + default 128 if !TICKLESS_KERNEL && (SILABS_SLEEPTIMER_TIMER || GECKO_BURTC_TIMER) + default 32768 if SILABS_SLEEPTIMER_TIMER + default 1024 if GECKO_BURTC_TIMER configdefault SILABS_SLEEPTIMER_TIMER default y