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