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