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