Skip to content

Commit 84f7098

Browse files
HoZHelcfriedt
authored andcommitted
drivers: timer: Provide radio timer driver for STM32WB0x SoCs
Provide radio timer driver for STM32WB0x SoCs. Signed-off-by: Ali Hozhabri <[email protected]>
1 parent f965565 commit 84f7098

File tree

1 file changed

+195
-0
lines changed

1 file changed

+195
-0
lines changed
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*
2+
* Copyright (c) 2025 STMicroelectronics
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#include <cmsis_core.h>
7+
#include <zephyr/drivers/counter.h>
8+
#include <zephyr/drivers/timer/system_timer.h>
9+
#include <zephyr/init.h>
10+
#include <zephyr/irq.h>
11+
#include <zephyr/kernel.h>
12+
#include <zephyr/spinlock.h>
13+
#include <zephyr/sys_clock.h>
14+
#include <zephyr/sys/util.h>
15+
#include "stm32wb0x_hal_radio_timer.h"
16+
17+
#include <zephyr/logging/log.h>
18+
LOG_MODULE_REGISTER(radio_timer_driver);
19+
20+
/* Max HS startup time expressed in system time (1953 us / 2.4414 us) */
21+
#define MAX_HS_STARTUP_TIME 320
22+
#define BLE_WKUP_PRIO 0
23+
#define CPU_WKUP_PRIO 1
24+
#define RADIO_TIMER_ERROR_PRIO 3
25+
26+
#define MULT64_THR_FREQ 806
27+
#define TIMER_WRAPPING_MARGIN 4096
28+
#define MAX_ALLOWED_DELAY (UINT32_MAX - TIMER_WRAPPING_MARGIN)
29+
#define MIN_ALLOWED_DELAY 32
30+
#define TIMER_ROUNDING 8
31+
32+
BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(clk_lsi), disabled),
33+
"LSI is not supported yet");
34+
35+
/* This value is only valid for the LSE with a frequency of 32768 Hz.
36+
* The implementation for the LSI will be done in the future.
37+
*/
38+
static const uint32_t calibration_data_freq1 = 0x0028F5C2;
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+
43+
static uint64_t announced_cycles;
44+
45+
static void radio_timer_error_isr(void *args)
46+
{
47+
volatile uint32_t debug_cmd;
48+
49+
ARG_UNUSED(args);
50+
51+
BLUE->DEBUGCMDREG |= 1;
52+
/* If the device is configured with CLK_SYS = 64MHz
53+
* and BLE clock = 16MHz, a register read is necessary
54+
* to ensure interrupt register is properly cleared
55+
* due to AHB down converter latency
56+
*/
57+
debug_cmd = BLUE->DEBUGCMDREG;
58+
LOG_ERR("Timer error");
59+
}
60+
61+
static void radio_timer_cpu_wkup_isr(void *args)
62+
{
63+
int32_t dticks;
64+
uint64_t diff_cycles;
65+
66+
ARG_UNUSED(args);
67+
68+
HAL_RADIO_TIMER_TimeoutCallback();
69+
if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
70+
diff_cycles = HAL_RADIO_TIMER_GetCurrentSysTime() - announced_cycles;
71+
dticks = (int32_t)k_cyc_to_ticks_near64(diff_cycles);
72+
announced_cycles += k_ticks_to_cyc_near32(dticks);
73+
sys_clock_announce(dticks);
74+
} else {
75+
sys_clock_announce(1);
76+
}
77+
}
78+
79+
#if defined(CONFIG_SOC_STM32WB06XX) || defined(CONFIG_SOC_STM32WB07XX)
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_STM32WB06XX || CONFIG_SOC_STM32WB07XX */
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 (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
97+
uint32_t current_time, delay;
98+
99+
ticks = MAX(1, ticks);
100+
delay = blue_unit_conversion(k_ticks_to_cyc_near32(ticks), calibration_data_freq1,
101+
MULT64_THR_FREQ);
102+
if (delay > MAX_ALLOWED_DELAY) {
103+
delay = MAX_ALLOWED_DELAY;
104+
} else {
105+
delay = MAX(MIN_ALLOWED_DELAY, delay);
106+
}
107+
current_time = LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP);
108+
LL_RADIO_TIMER_SetCPUWakeupTime(WAKEUP, current_time + delay + TIMER_ROUNDING);
109+
LL_RADIO_TIMER_EnableCPUWakeupTimer(WAKEUP);
110+
}
111+
}
112+
113+
uint32_t sys_clock_elapsed(void)
114+
{
115+
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
116+
return 0;
117+
}
118+
119+
return k_cyc_to_ticks_near32(HAL_RADIO_TIMER_GetCurrentSysTime() - announced_cycles);
120+
}
121+
122+
uint32_t sys_clock_cycle_get_32(void)
123+
{
124+
return sys_clock_cycle_get_64() & UINT32_MAX;
125+
}
126+
127+
uint64_t sys_clock_cycle_get_64(void)
128+
{
129+
return HAL_RADIO_TIMER_GetCurrentSysTime();
130+
}
131+
132+
void sys_clock_disable(void)
133+
{
134+
#if defined(CONFIG_SOC_STM32WB06XX) || defined(CONFIG_SOC_STM32WB07XX)
135+
irq_disable(RADIO_TIMER_TXRX_WKUP_IRQn);
136+
#endif /* CONFIG_SOC_STM32WB06XX || CONFIG_SOC_STM32WB07XX */
137+
irq_disable(RADIO_TIMER_CPU_WKUP_IRQn);
138+
irq_disable(RADIO_TIMER_ERROR_IRQn);
139+
__HAL_RCC_RADIO_CLK_DISABLE();
140+
}
141+
142+
void sys_clock_idle_exit(void)
143+
{
144+
#if defined(CONFIG_SOC_STM32WB06XX) || defined(CONFIG_SOC_STM32WB07XX)
145+
irq_enable(RADIO_TIMER_TXRX_WKUP_IRQn);
146+
#endif /* CONFIG_SOC_STM32WB06XX || CONFIG_SOC_STM32WB07XX */
147+
148+
irq_enable(RADIO_TIMER_CPU_WKUP_IRQn);
149+
irq_enable(RADIO_TIMER_ERROR_IRQn);
150+
}
151+
152+
static int sys_clock_driver_init(void)
153+
{
154+
RADIO_TIMER_InitTypeDef vtimer_init_struct = {MAX_HS_STARTUP_TIME, 0, 0};
155+
156+
#if defined(CONFIG_SOC_STM32WB06XX) || defined(CONFIG_SOC_STM32WB07XX)
157+
IRQ_CONNECT(RADIO_TIMER_TXRX_WKUP_IRQn,
158+
BLE_WKUP_PRIO,
159+
radio_timer_txrx_wkup_isr,
160+
NULL,
161+
0);
162+
#endif /* CONFIG_SOC_STM32WB06XX || CONFIG_SOC_STM32WB07XX */
163+
164+
IRQ_CONNECT(RADIO_TIMER_CPU_WKUP_IRQn,
165+
CPU_WKUP_PRIO,
166+
radio_timer_cpu_wkup_isr,
167+
NULL,
168+
0);
169+
IRQ_CONNECT(RADIO_TIMER_ERROR_IRQn,
170+
RADIO_TIMER_ERROR_PRIO,
171+
radio_timer_error_isr,
172+
NULL,
173+
0);
174+
175+
/* Peripheral clock enable */
176+
if (__HAL_RCC_RADIO_IS_CLK_DISABLED()) {
177+
/* Radio reset */
178+
__HAL_RCC_RADIO_FORCE_RESET();
179+
__HAL_RCC_RADIO_RELEASE_RESET();
180+
181+
/* Enable Radio peripheral clock */
182+
__HAL_RCC_RADIO_CLK_ENABLE();
183+
}
184+
185+
/* Wait to be sure that the Radio Timer is active */
186+
while (LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP) < 0x10) {
187+
}
188+
189+
/* Device IRQs are enabled by this function. */
190+
HAL_RADIO_TIMER_Init(&vtimer_init_struct);
191+
LL_RADIO_TIMER_EnableWakeupTimerLowPowerMode(WAKEUP);
192+
return 0;
193+
}
194+
195+
SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);

0 commit comments

Comments
 (0)