1 /*
2 * Copyright (c) 2023 Antmicro <www.antmicro.com>
3 *
4 * Based on:
5 * sam0_rtc_timer.c Copyright (c) 2018 omSquare s.r.o.
6 * intel_adsp_timer.c Copyright (c) 2020 Intel Corporation
7 *
8 * SPDX-License-Identifier: Apache-2.0
9 */
10
11 #define DT_DRV_COMPAT silabs_gecko_burtc
12
13 /**
14 * @file
15 * @brief SiLabs Gecko BURTC-based sys_clock driver
16 *
17 */
18
19 #include <zephyr/init.h>
20 #include <soc.h>
21 #include <zephyr/drivers/clock_control.h>
22 #include <zephyr/drivers/timer/system_timer.h>
23 #include <zephyr/drivers/pinctrl.h>
24 #include <zephyr/sys_clock.h>
25 #include <zephyr/irq.h>
26 #include <zephyr/spinlock.h>
27 #include <zephyr/logging/log.h>
28
29 #include "em_device.h"
30 #include "em_cmu.h"
31 #include "em_burtc.h"
32
33
34 LOG_MODULE_REGISTER(gecko_burtc_timer);
35
36
37 /* Maximum time interval between timer interrupts (in hw_cycles) */
38 #define MAX_TIMEOUT_CYC (UINT32_MAX >> 1)
39
40 /*
41 * Mininum time interval between now and IRQ firing that can be scheduled.
42 * The main cause for this is LFSYNC register update, which requires several
43 * LF clk cycles for synchronization.
44 * Seee e.g. "4.2.4.4.4 LFSYNC Registers" in "EFR32xG22 Reference Manual"
45 */
46 #define MIN_DELAY_CYC (6u)
47
48 #define TIMER_IRQ (DT_INST_IRQN(0))
49
50 #if defined(CONFIG_TEST)
51 /* See tests/kernel/context */
52 const int32_t z_sys_timer_irq_for_test = TIMER_IRQ;
53 #endif
54
55 /* With CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME, that's where we
56 * should write hw_cycles timer clock frequency upon init
57 */
58 extern int z_clock_hw_cycles_per_sec;
59
60 /* Number of hw_cycles clocks per 1 kernel tick */
61 static uint32_t g_cyc_per_tick;
62
63 /* MAX_TIMEOUT_CYC expressed as ticks */
64 static uint32_t g_max_timeout_ticks;
65
66 /* Value of BURTC counter when the previous kernel tick was announced */
67 static atomic_t g_last_count;
68
69 /* Spinlock to sync between Compare ISR and update of Compare register */
70 static struct k_spinlock g_lock;
71
72 /* Set to true when timer is initialized */
73 static bool g_init;
74
burtc_isr(const void * arg)75 static void burtc_isr(const void *arg)
76 {
77 ARG_UNUSED(arg);
78
79 /* Clear the interrupt */
80 BURTC_IntClear(BURTC_IF_COMP);
81
82 uint32_t curr = BURTC_CounterGet();
83
84 /* NOTE: this is the only place where g_last_count is modified,
85 * so we don't need to do make the whole read-and-modify atomic, just
86 * writing it behind the memory barrier is enough
87 */
88 uint32_t prev = atomic_get(&g_last_count);
89
90 /* How many ticks have we not announced since the last announcement */
91 uint32_t unannounced = (curr - prev) / g_cyc_per_tick;
92
93 atomic_set(&g_last_count, prev + unannounced * g_cyc_per_tick);
94
95 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
96 /* Counter value on which announcement should be made */
97 uint32_t next = prev + g_cyc_per_tick;
98
99 /* `next` can be too close in the future since we're trying to
100 * announce the very next tick - in that case we skip one and
101 * announce the one after it instead
102 */
103 if ((next - curr) < MIN_DELAY_CYC) {
104 next += g_cyc_per_tick;
105 }
106
107 BURTC_CompareSet(0, next);
108 }
109
110 sys_clock_announce(unannounced);
111 }
112
sys_clock_set_timeout(int32_t ticks,bool idle)113 void sys_clock_set_timeout(int32_t ticks, bool idle)
114 {
115 ARG_UNUSED(idle);
116
117 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
118 return;
119 }
120
121 /*
122 * calculate 'ticks' value that specifies which tick to announce,
123 * beginning from the closest upcoming one:
124 * 0 - announce upcoming tick itself
125 * 1 - skip upcoming one, but announce the one after it, etc.
126 */
127 ticks = (ticks == K_TICKS_FOREVER) ? g_max_timeout_ticks : ticks;
128 ticks = CLAMP(ticks - 1, 0, g_max_timeout_ticks);
129
130 k_spinlock_key_t key = k_spin_lock(&g_lock);
131
132 uint32_t curr = BURTC_CounterGet();
133 uint32_t prev = atomic_get(&g_last_count);
134
135 /* How many ticks have we not announced since the last announcement */
136 uint32_t unannounced = (curr - prev) / g_cyc_per_tick;
137
138 /* Which tick to announce (counting from the last announced one) */
139 uint32_t to_announce = unannounced + ticks + 1;
140
141 /* Force maximum interval between announcements. If we sit without
142 * announcements for too long, counter will roll over and we'll lose
143 * track of unannounced ticks.
144 */
145 to_announce = MIN(to_announce, g_max_timeout_ticks);
146
147 /* Counter value on which announcement should be made */
148 uint32_t next = prev + to_announce * g_cyc_per_tick;
149
150 /* `next` can be too close in the future if we're trying to announce
151 * the very next tick - in that case we skip one and announce the one
152 * after it instead
153 */
154 if ((next - curr) < MIN_DELAY_CYC) {
155 next += g_cyc_per_tick;
156 }
157
158 BURTC_CompareSet(0, next);
159 k_spin_unlock(&g_lock, key);
160 }
161
sys_clock_elapsed(void)162 uint32_t sys_clock_elapsed(void)
163 {
164 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL) || !g_init) {
165 return 0;
166 } else {
167 return (BURTC_CounterGet() - g_last_count) / g_cyc_per_tick;
168 }
169 }
170
sys_clock_cycle_get_32(void)171 uint32_t sys_clock_cycle_get_32(void)
172 {
173 /* API note: this function is unrelated to kernel ticks, it returns
174 * a value of some 32-bit hw_cycles counter which counts with
175 * z_clock_hw_cycles_per_sec frequency
176 */
177 if (!g_init) {
178 return 0;
179 } else {
180 return BURTC_CounterGet();
181 }
182 }
183
burtc_init(void)184 static int burtc_init(void)
185 {
186 uint32_t hw_clock_freq;
187 BURTC_Init_TypeDef init = BURTC_INIT_DEFAULT;
188
189 /* Enable clock for BURTC CSRs on APB */
190 CMU_ClockEnable(cmuClock_BURTC, true);
191
192 /* Configure BURTC LF clocksource according to Kconfig */
193 #if defined(CONFIG_CMU_BURTCCLK_LFXO)
194 CMU_ClockSelectSet(cmuClock_BURTC, cmuSelect_LFXO);
195 #elif defined(CONFIG_CMU_BURTCCLK_LFRCO)
196 CMU_ClockSelectSet(cmuClock_BURTC, cmuSelect_LFRCO);
197 #elif defined(CONFIG_CMU_BURTCCLK_ULFRCO)
198 CMU_ClockSelectSet(cmuClock_BURTC, cmuSelect_ULFRCO);
199 #else
200 #error "Unsupported BURTC clock specified"
201 #endif
202
203 /* Calculate timing constants and init BURTC */
204 hw_clock_freq = CMU_ClockFreqGet(cmuClock_BURTC);
205 z_clock_hw_cycles_per_sec = hw_clock_freq;
206
207 BUILD_ASSERT(CONFIG_SYS_CLOCK_TICKS_PER_SEC > 0,
208 "Invalid CONFIG_SYS_CLOCK_TICKS_PER_SEC value");
209 g_cyc_per_tick = hw_clock_freq / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
210
211 __ASSERT(g_cyc_per_tick >= MIN_DELAY_CYC,
212 "%u cycle-long tick is too short to be scheduled "
213 "(min is %u). Config: SYS_CLOCK_TICKS_PER_SEC is "
214 "%d and timer frequency is %u",
215 g_cyc_per_tick, MIN_DELAY_CYC, CONFIG_SYS_CLOCK_TICKS_PER_SEC,
216 hw_clock_freq);
217
218 g_max_timeout_ticks = MAX_TIMEOUT_CYC / g_cyc_per_tick;
219
220 init.clkDiv = 1;
221 init.start = false;
222 BURTC_Init(&init);
223 g_init = true;
224
225 /* Enable compare match interrupt */
226 BURTC_IntClear(BURTC_IF_COMP);
227 BURTC_IntEnable(BURTC_IF_COMP);
228 NVIC_ClearPendingIRQ(TIMER_IRQ);
229 IRQ_CONNECT(TIMER_IRQ, DT_INST_IRQ(0, priority), burtc_isr, 0, 0);
230 irq_enable(TIMER_IRQ);
231
232 /* Start the timer and announce 1 kernel tick */
233 atomic_set(&g_last_count, 0);
234 BURTC_CompareSet(0, g_cyc_per_tick);
235
236 BURTC_SyncWait();
237 BURTC->CNT = 0;
238 BURTC_Start();
239
240 return 0;
241 }
242
243 SYS_INIT(burtc_init, PRE_KERNEL_2,
244 CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
245