|
| 1 | +/* stm32_radio_timer.c - STM32WB0x Radio Timer */ |
| 2 | + |
| 3 | +/* |
| 4 | + * Copyright (c) 2025 STMicroelectronics |
| 5 | + * |
| 6 | + * SPDX-License-Identifier: Apache-2.0 |
| 7 | + */ |
| 8 | +#include <zephyr/kernel.h> |
| 9 | +#include <zephyr/init.h> |
| 10 | +#include <zephyr/drivers/timer/system_timer.h> |
| 11 | +#include <zephyr/sys_clock.h> |
| 12 | +#include <zephyr/spinlock.h> |
| 13 | +#include <cmsis_core.h> |
| 14 | +#include <zephyr/irq.h> |
| 15 | +#include <zephyr/sys/util.h> |
| 16 | +#include <zephyr/drivers/counter.h> |
| 17 | +#include "stm32wb0x_hal_radio_timer.h" |
| 18 | + |
| 19 | +#include <zephyr/logging/log.h> |
| 20 | +LOG_MODULE_REGISTER(radio_timer_driver); |
| 21 | + |
| 22 | +/* Max HS startup time expressed in system time (1953 us / 2.4414 us) */ |
| 23 | +#define MAX_HS_STARTUP_TIME 320 |
| 24 | +#define BLE_WKUP_PRIO 0 |
| 25 | +#define BLE_WKUP_FLAGS 0 |
| 26 | +#define CPU_WKUP_PRIO 1 |
| 27 | +#define CPU_WKUP_FLAGS 0 |
| 28 | +#define RADIO_TIMER_ERROR_PRIO 3 |
| 29 | +#define RADIO_TIMER_ERROR_FLAGS 0 |
| 30 | + |
| 31 | +#define MULT64_THR_FREQ (806) |
| 32 | +#define TIMER_MAX_VALUE (0xFFFFFFFFU) |
| 33 | +#define TIMER_WRAPPING_MARGIN (4096) |
| 34 | +#define MAX_ALLOWED_DELAY (TIMER_MAX_VALUE - TIMER_WRAPPING_MARGIN) |
| 35 | + |
| 36 | + |
| 37 | +BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(clk_lsi), disabled), |
| 38 | + "At the moment, LSI is not supported"); |
| 39 | + |
| 40 | +/* Translate STU to MTU and vice versa. It is implemented by using integer operations. */ |
| 41 | +uint32_t blue_unit_conversion(uint32_t time, uint32_t period_freq, uint32_t thr); |
| 42 | +static uint64_t announced_cycles; |
| 43 | + |
| 44 | +static void radio_timer_error_isr(void *args) |
| 45 | +{ |
| 46 | + volatile uint32_t debug_cmd; |
| 47 | + |
| 48 | + ARG_UNUSED(args); |
| 49 | + |
| 50 | + BLUE->DEBUGCMDREG |= 1; |
| 51 | + /* If the device is configured with CLK_SYS = 64MHz |
| 52 | + * and BLE clock = 16MHz, a register read is necessary |
| 53 | + * to ensure interrupt register is properly cleared |
| 54 | + * due to AHB down converter latency |
| 55 | + */ |
| 56 | + debug_cmd = BLUE->DEBUGCMDREG; |
| 57 | + LOG_ERR("Timer error"); |
| 58 | +} |
| 59 | + |
| 60 | +static void radio_timer_cpu_wkup_isr(void *args) |
| 61 | +{ |
| 62 | + int32_t dticks; |
| 63 | + uint64_t diff_cycles; |
| 64 | + |
| 65 | + ARG_UNUSED(args); |
| 66 | + |
| 67 | + HAL_RADIO_TIMER_TimeoutCallback(); |
| 68 | + if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { |
| 69 | + diff_cycles = HAL_RADIO_TIMER_GetCurrentSysTime() - announced_cycles; |
| 70 | + dticks = (int32_t)k_cyc_to_ticks_near64(diff_cycles); |
| 71 | + announced_cycles += k_ticks_to_cyc_near32(dticks); |
| 72 | + sys_clock_announce(dticks); |
| 73 | + } else { |
| 74 | + sys_clock_announce(1); |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +volatile uint32_t ciccio_k = 0; |
| 79 | +#if defined(CONFIG_SOC_STM32WB06) || defined(CONFIG_SOC_STM32WB07) |
| 80 | +static void radio_timer_txrx_wkup_isr(void *args) |
| 81 | +{ |
| 82 | + ARG_UNUSED(args); |
| 83 | + |
| 84 | + HAL_RADIO_TIMER_WakeUpCallback(); |
| 85 | +} |
| 86 | +#endif /* CONFIG_SOC_STM32WB06 || CONFIG_SOC_STM32WB07 */ |
| 87 | + |
| 88 | +void sys_clock_set_timeout(int32_t ticks, bool idle) |
| 89 | +{ |
| 90 | + ARG_UNUSED(idle); |
| 91 | + |
| 92 | + if (ticks == K_TICKS_FOREVER) { |
| 93 | + return; |
| 94 | + } |
| 95 | + |
| 96 | +#if defined(CONFIG_TICKLESS_KERNEL) |
| 97 | + uint32_t current_time, delay; |
| 98 | + |
| 99 | + /* This value is only valid for the LSE with a frequency of 32768 Hz. |
| 100 | + * The implementation for the LSI will be done in the future. |
| 101 | + */ |
| 102 | + uint32_t calibrationData_freq1 = 0x0028F5C2; |
| 103 | + |
| 104 | + if (ticks < 1) { |
| 105 | + ticks = 1; |
| 106 | + } |
| 107 | + //CLAMP(ticks, 1, (int32_t)MAX_TICKS); |
| 108 | + delay = blue_unit_conversion(k_ticks_to_cyc_near32(ticks), calibrationData_freq1, MULT64_THR_FREQ) ; |
| 109 | + if (delay > MAX_ALLOWED_DELAY) { |
| 110 | + delay = MAX_ALLOWED_DELAY; |
| 111 | + } else { |
| 112 | + /* If the delay is too small round to minimum 2 ticks */ |
| 113 | + delay = MAX(32, delay); |
| 114 | + } |
| 115 | + current_time = LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP); |
| 116 | + /* 4 least significant bits are not taken into account. Then let's round the value */ |
| 117 | + LL_RADIO_TIMER_SetCPUWakeupTime(WAKEUP, ((current_time + (delay + 8)) & TIMER_MAX_VALUE)); |
| 118 | + LL_RADIO_TIMER_EnableWakeupTimerLowPowerMode(WAKEUP); |
| 119 | + LL_RADIO_TIMER_EnableCPUWakeupTimer(WAKEUP); |
| 120 | + |
| 121 | +#endif /* CONFIG_TICKLESS_KERNEL */ |
| 122 | +} |
| 123 | + |
| 124 | +uint32_t sys_clock_elapsed(void) |
| 125 | +{ |
| 126 | + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { |
| 127 | + return 0; |
| 128 | + } |
| 129 | + |
| 130 | + return k_cyc_to_ticks_near32(HAL_RADIO_TIMER_GetCurrentSysTime() - announced_cycles); |
| 131 | +} |
| 132 | + |
| 133 | +uint32_t sys_clock_cycle_get_32(void) |
| 134 | +{ |
| 135 | + uint64_t val64; |
| 136 | + |
| 137 | + val64 = sys_clock_cycle_get_64(); |
| 138 | + return val64 & 0xFFFFFFFF; |
| 139 | +} |
| 140 | + |
| 141 | +uint64_t sys_clock_cycle_get_64(void) |
| 142 | +{ |
| 143 | + uint64_t cycles; |
| 144 | + |
| 145 | + cycles = HAL_RADIO_TIMER_GetCurrentSysTime(); |
| 146 | + return cycles; |
| 147 | +} |
| 148 | + |
| 149 | +void sys_clock_disable(void) |
| 150 | +{ |
| 151 | +#if defined(CONFIG_SOC_STM32WB06) || defined(CONFIG_SOC_STM32WB07) |
| 152 | + irq_disable(RADIO_TIMER_TXRX_WKUP_IRQn); |
| 153 | +#endif /* CONFIG_SOC_STM32WB06 || CONFIG_SOC_STM32WB07 */ |
| 154 | + irq_disable(RADIO_TIMER_CPU_WKUP_IRQn); |
| 155 | + irq_disable(RADIO_TIMER_ERROR_IRQn); |
| 156 | + __HAL_RCC_RADIO_CLK_DISABLE(); |
| 157 | +} |
| 158 | + |
| 159 | +static int sys_clock_driver_init(void) |
| 160 | +{ |
| 161 | + RADIO_TIMER_InitTypeDef VTIMER_InitStruct = {MAX_HS_STARTUP_TIME, 0, 0}; |
| 162 | + |
| 163 | +#if defined(CONFIG_SOC_STM32WB06) || defined(CONFIG_SOC_STM32WB07) |
| 164 | + IRQ_CONNECT(RADIO_TIMER_TXRX_WKUP_IRQn, |
| 165 | + BLE_WKUP_PRIO, |
| 166 | + radio_timer_txrx_wkup_isr, |
| 167 | + NULL, |
| 168 | + BLE_WKUP_FLAGS); |
| 169 | +#endif /* CONFIG_SOC_STM32WB06 || CONFIG_SOC_STM32WB07 */ |
| 170 | + |
| 171 | + IRQ_CONNECT(RADIO_TIMER_CPU_WKUP_IRQn, |
| 172 | + CPU_WKUP_PRIO, |
| 173 | + radio_timer_cpu_wkup_isr, |
| 174 | + NULL, |
| 175 | + CPU_WKUP_FLAGS); |
| 176 | + IRQ_CONNECT(RADIO_TIMER_ERROR_IRQn, |
| 177 | + RADIO_TIMER_ERROR_PRIO, |
| 178 | + radio_timer_error_isr, |
| 179 | + NULL, |
| 180 | + RADIO_TIMER_ERROR_FLAGS); |
| 181 | + |
| 182 | + /* Peripheral clock enable */ |
| 183 | + if (__HAL_RCC_RADIO_IS_CLK_DISABLED()) { |
| 184 | + /* Radio reset */ |
| 185 | + __HAL_RCC_RADIO_FORCE_RESET(); |
| 186 | + __HAL_RCC_RADIO_RELEASE_RESET(); |
| 187 | + |
| 188 | + /* Enable Radio peripheral clock */ |
| 189 | + __HAL_RCC_RADIO_CLK_ENABLE(); |
| 190 | + } |
| 191 | + |
| 192 | + /* Wait to be sure that the Radio Timer is active */ |
| 193 | + while (LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP) < 0x10) { |
| 194 | + } |
| 195 | + HAL_RADIO_TIMER_Init(&VTIMER_InitStruct); |
| 196 | + return 0; |
| 197 | +} |
| 198 | + |
| 199 | +SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); |
0 commit comments