blob: 8508c1c53bfb2cfdd10f8d41cf1a17a546b8e3e8 [file] [log] [blame]
/*
* Copyright (c) 2025 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <cmsis_core.h>
#include <zephyr/drivers/counter.h>
#include <zephyr/drivers/timer/system_timer.h>
#include <zephyr/init.h>
#include <zephyr/irq.h>
#include <zephyr/kernel.h>
#include <zephyr/spinlock.h>
#include <zephyr/sys_clock.h>
#include <zephyr/sys/util.h>
#include "stm32wb0x_hal_radio_timer.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(radio_timer_driver);
/* Max HS startup time expressed in system time (1953 us / 2.4414 us) */
#define MAX_HS_STARTUP_TIME DT_PROP(DT_NODELABEL(radio_timer), max_hs_startup_time)
#define BLE_WKUP_PRIO 0
#define CPU_WKUP_PRIO 1
#define RADIO_TIMER_ERROR_PRIO 3
#define MULT64_THR_FREQ 806
#define TIMER_WRAPPING_MARGIN 4096
#define MAX_ALLOWED_DELAY (UINT32_MAX - TIMER_WRAPPING_MARGIN)
#define MIN_ALLOWED_DELAY 32
#define TIMER_ROUNDING 8
BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(clk_lsi), disabled),
"LSI is not supported yet");
#if (defined(CONFIG_SOC_STM32WB06XX) || defined(CONFIG_SOC_STM32WB06XX)) && defined(CONFIG_PM)
#error "PM is not supported yet for WB06/WB07"
#endif /* (CONFIG_SOC_STM32WB06XX || CONFIG_SOC_STM32WB06XX) && CONFIG_PM */
/* This value is only valid for the LSE with a frequency of 32768 Hz.
* The implementation for the LSI will be done in the future.
*/
static const uint32_t calibration_data_freq1 = 0x0028F5C2;
/* Translate STU to MTU and vice versa. It is implemented by using integer operations. */
uint32_t blue_unit_conversion(uint32_t time, uint32_t period_freq, uint32_t thr);
static uint64_t announced_cycles;
static void radio_timer_error_isr(void *args)
{
volatile uint32_t debug_cmd;
ARG_UNUSED(args);
BLUE->DEBUGCMDREG |= 1;
/* If the device is configured with CLK_SYS = 64MHz
* and BLE clock = 16MHz, a register read is necessary
* to ensure interrupt register is properly cleared
* due to AHB down converter latency
*/
debug_cmd = BLUE->DEBUGCMDREG;
LOG_ERR("Timer error");
}
static void radio_timer_cpu_wkup_isr(void *args)
{
int32_t dticks;
uint64_t diff_cycles;
ARG_UNUSED(args);
HAL_RADIO_TIMER_TimeoutCallback();
if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
diff_cycles = HAL_RADIO_TIMER_GetCurrentSysTime() - announced_cycles;
dticks = (int32_t)k_cyc_to_ticks_near64(diff_cycles);
announced_cycles += k_ticks_to_cyc_near32(dticks);
sys_clock_announce(dticks);
} else {
sys_clock_announce(1);
}
}
#if defined(CONFIG_SOC_STM32WB06XX) || defined(CONFIG_SOC_STM32WB07XX)
static void radio_timer_txrx_wkup_isr(void *args)
{
ARG_UNUSED(args);
HAL_RADIO_TIMER_WakeUpCallback();
}
#endif /* CONFIG_SOC_STM32WB06XX || CONFIG_SOC_STM32WB07XX */
void sys_clock_set_timeout(int32_t ticks, bool idle)
{
ARG_UNUSED(idle);
if (ticks == K_TICKS_FOREVER) {
return;
}
if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
uint32_t current_time, delay;
ticks = MAX(1, ticks);
delay = blue_unit_conversion(k_ticks_to_cyc_near32(ticks), calibration_data_freq1,
MULT64_THR_FREQ);
if (delay > MAX_ALLOWED_DELAY) {
delay = MAX_ALLOWED_DELAY;
} else {
delay = MAX(MIN_ALLOWED_DELAY, delay);
}
current_time = LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP);
LL_RADIO_TIMER_SetCPUWakeupTime(WAKEUP, current_time + delay + TIMER_ROUNDING);
LL_RADIO_TIMER_EnableCPUWakeupTimer(WAKEUP);
}
}
uint32_t sys_clock_elapsed(void)
{
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
return 0;
}
return k_cyc_to_ticks_near32(HAL_RADIO_TIMER_GetCurrentSysTime() - announced_cycles);
}
uint32_t sys_clock_cycle_get_32(void)
{
return sys_clock_cycle_get_64() & UINT32_MAX;
}
uint64_t sys_clock_cycle_get_64(void)
{
return HAL_RADIO_TIMER_GetCurrentSysTime();
}
void sys_clock_disable(void)
{
#if defined(CONFIG_SOC_STM32WB06XX) || defined(CONFIG_SOC_STM32WB07XX)
irq_disable(RADIO_TIMER_TXRX_WKUP_IRQn);
#endif /* CONFIG_SOC_STM32WB06XX || CONFIG_SOC_STM32WB07XX */
irq_disable(RADIO_TIMER_CPU_WKUP_IRQn);
irq_disable(RADIO_TIMER_ERROR_IRQn);
__HAL_RCC_RADIO_CLK_DISABLE();
}
void sys_clock_idle_exit(void)
{
#if defined(CONFIG_SOC_STM32WB06XX) || defined(CONFIG_SOC_STM32WB07XX)
irq_enable(RADIO_TIMER_TXRX_WKUP_IRQn);
#endif /* CONFIG_SOC_STM32WB06XX || CONFIG_SOC_STM32WB07XX */
irq_enable(RADIO_TIMER_CPU_WKUP_IRQn);
irq_enable(RADIO_TIMER_ERROR_IRQn);
}
static int sys_clock_driver_init(void)
{
RADIO_TIMER_InitTypeDef vtimer_init_struct = {MAX_HS_STARTUP_TIME, 0, 0};
#if defined(CONFIG_SOC_STM32WB06XX) || defined(CONFIG_SOC_STM32WB07XX)
IRQ_CONNECT(RADIO_TIMER_TXRX_WKUP_IRQn,
BLE_WKUP_PRIO,
radio_timer_txrx_wkup_isr,
NULL,
0);
#endif /* CONFIG_SOC_STM32WB06XX || CONFIG_SOC_STM32WB07XX */
IRQ_CONNECT(RADIO_TIMER_CPU_WKUP_IRQn,
CPU_WKUP_PRIO,
radio_timer_cpu_wkup_isr,
NULL,
0);
IRQ_CONNECT(RADIO_TIMER_ERROR_IRQn,
RADIO_TIMER_ERROR_PRIO,
radio_timer_error_isr,
NULL,
0);
/* Peripheral clock enable */
if (__HAL_RCC_RADIO_IS_CLK_DISABLED()) {
/* Radio reset */
__HAL_RCC_RADIO_FORCE_RESET();
__HAL_RCC_RADIO_RELEASE_RESET();
/* Enable Radio peripheral clock */
__HAL_RCC_RADIO_CLK_ENABLE();
}
/* Wait to be sure that the Radio Timer is active */
while (LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP) < 0x10) {
}
/* Device IRQs are enabled by this function. */
HAL_RADIO_TIMER_Init(&vtimer_init_struct);
LL_RADIO_TIMER_EnableWakeupTimerLowPowerMode(WAKEUP);
return 0;
}
SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);