1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * nRF54L
9 * CLOCK - Clock control
10 * POWER - POWER control
11 * RESET - RESET control
12 *
13 * Note: CLOCK:
14 * 1. This model ignores the system ON/OFF status
15 *
16 * 2. We assume the 32.768 KHz clock does not drift relative to the 64MHz one.
17 * This would only be the case if they had the same source or there was a tracking
18 * and adjustment loop of one based on the other.
19 *
20 * 3. All tasks complete in 1 delta cycle
21 *
22 * 4. LFCLK.SRC is ignored (beyond copying LFCLK.SRC to LFCLK.SRCCOPY and LFCLK.STAT)
23 *
24 * 5. Turning the clocks on/off has no effect on other peripherals models. Other peripherals
25 * do not request clocks to this model.
26 * So triggering a clock STOP task will always be done right away, without needing
27 * to wait for a peripheral which is using that clock to stop requesting it.
28 *
29 * There is no relationship to the OSCILLATORS peripheral yet.
30 *
31 * 6. From the spec, it is unclear if the *.RUN.STATUS register fields are kept on even
32 * after the operation is completed or they are cleared.
33 * The model just leaves them at 1, unless the STOP task is triggered.
34 *
35 * 7. XOTUNE does nothing more than generate the XOTUNED event in one delta. It cannot fail.
36 * the events XOTUNEERROR and XOTUNEFAILED are never generated.
37 * XOTUNEABORT does nothing.
38 *
39 * 8. The models do not check the requirement of having the HFXO clock running to be
40 * able to run the RADIO. The RADIO models will run just fine without it.
41 *
42 * Notes: POWER & RESET
43 * * Only a register stub without functionality is provided
44 */
45
46 #include <string.h>
47 #include <stdint.h>
48 #include "NHW_config.h"
49 #include "NHW_peri_types.h"
50 #include "NHW_54L_CLOCK.h"
51 #include "NHW_templates.h"
52 #include "NHW_xPPI.h"
53 #include "irq_ctrl.h"
54 #include "bs_tracing.h"
55 #include "bs_utils.h"
56 #include "nsi_tasks.h"
57 #include "nsi_hws_models_if.h"
58
59 union NRF_CLKPWR_Type NRF_CLKPWR_regs[NHW_CLKPWR_TOTAL_INST];
60
61 NRF_CLOCK_Type *NRF_CLOCK_regs[NHW_CLKPWR_TOTAL_INST];
62 NRF_POWER_Type *NRF_POWER_regs[NHW_CLKPWR_TOTAL_INST];
63 NRF_RESET_Type *NRF_RESET_regs[NHW_CLKPWR_TOTAL_INST];
64
65 enum clock_states {Stopped = 0, Starting, Started, Stopping};
66
67 struct clkpwr_status {
68 bs_time_t Timer_XO;
69 bs_time_t Timer_PLL;
70 bs_time_t Timer_LFCLK;
71 bs_time_t Timer_CAL;
72 bs_time_t Timer_XOTUNE;
73
74 enum clock_states XO_state;
75 enum clock_states LFCLK_state;
76 enum clock_states PLL_state;
77 enum clock_states CAL_state;
78 enum clock_states XOTUNE_state;
79 };
80
81 static bs_time_t Timer_PWRCLK = TIME_NEVER;
82 static struct clkpwr_status nhw_clkpwr_st;
83 static uint nhw_CLOCK_dppi_map[] = NHW_CLKPWR_DPPI_MAP;
84
85 static void nhw_CLOCK_XOTimer_triggered(void);
86 static void nhw_CLOCK_LFCLK_triggered(void);
87 static void nhw_CLOCK_PLLTimer_triggered(void);
88
nhw_CLOCK_update_master_timer(void)89 static void nhw_CLOCK_update_master_timer(void) {
90
91 Timer_PWRCLK = TIME_NEVER;
92
93 bs_time_t t1 = BS_MIN(nhw_clkpwr_st.Timer_XO, nhw_clkpwr_st.Timer_PLL);
94 bs_time_t t2 = BS_MIN(nhw_clkpwr_st.Timer_LFCLK, nhw_clkpwr_st.Timer_CAL);
95
96 bs_time_t el_min = BS_MIN(t1, t2);
97 if (el_min < Timer_PWRCLK) {
98 Timer_PWRCLK = el_min;
99 }
100 nsi_hws_find_next_event();
101 }
102
nhw_CLOCK_init(void)103 static void nhw_CLOCK_init(void) {
104 NRF_CLOCK_regs[0] = (NRF_CLOCK_Type *)&NRF_CLKPWR_regs[0];
105 NRF_POWER_regs[0] = (NRF_POWER_Type *)&NRF_CLKPWR_regs[0];
106 NRF_RESET_regs[0] = (NRF_RESET_Type *)&NRF_CLKPWR_regs[0];
107
108 memset(NRF_CLKPWR_regs, 0, sizeof(NRF_CLKPWR_regs));
109
110 nhw_clkpwr_st.Timer_XO = TIME_NEVER;
111 nhw_clkpwr_st.Timer_PLL = TIME_NEVER;
112 nhw_clkpwr_st.Timer_LFCLK = TIME_NEVER;
113 nhw_clkpwr_st.Timer_CAL = TIME_NEVER;
114 nhw_clkpwr_st.Timer_XOTUNE= TIME_NEVER;
115
116 nhw_clkpwr_st.XO_state = Stopped;
117 nhw_clkpwr_st.LFCLK_state = Stopped;
118 nhw_clkpwr_st.PLL_state = Stopped;
119 nhw_clkpwr_st.CAL_state = Stopped;
120 nhw_clkpwr_st.XOTUNE_state= Stopped;
121
122 nhw_CLOCK_update_master_timer();
123 }
124
125 NSI_TASK(nhw_CLOCK_init, HW_INIT, 100);
126
nhw_CLOCK_eval_interrupt(uint inst)127 static void nhw_CLOCK_eval_interrupt(uint inst) {
128 (void) inst;
129 static struct nhw_irq_mapping nhw_CLOCK_irq_map[] = NHW_CLKPWR_INT_MAP;
130 static bool clock_int_line; /* Is the CLOCK currently driving its interrupt line high */
131 bool new_int_line = false;
132
133 NRF_CLOCK_regs[0]->INTPEND = 0;
134
135 #define check_interrupt(x) \
136 if (NRF_CLOCK_regs[0]->EVENTS_ ##x && (NRF_CLOCK_regs[0]->INTEN & CLOCK_INTENSET_## x ##_Msk)){ \
137 new_int_line = 1; \
138 NRF_CLOCK_regs[0]->INTPEND |= CLOCK_INTENSET_## x ##_Msk; \
139 }
140
141 check_interrupt(XOSTARTED)
142 check_interrupt(PLLSTARTED)
143 check_interrupt(LFCLKSTARTED)
144 check_interrupt(DONE)
145 check_interrupt(XOTUNED)
146 check_interrupt(XOTUNEERROR)
147 check_interrupt(XOTUNEFAILED)
148
149 hw_irq_ctrl_toggle_level_irq_line_if(&clock_int_line,
150 new_int_line,
151 &nhw_CLOCK_irq_map[0]);
152 }
153
nhw_CLOCK_TASK_XOSTART(uint inst)154 static void nhw_CLOCK_TASK_XOSTART(uint inst) {
155 (void) inst;
156 if ((nhw_clkpwr_st.XO_state == Stopped ) || (nhw_clkpwr_st.XO_state == Stopping)) {
157 nhw_clkpwr_st.XO_state = Starting;
158 NRF_CLOCK_regs[0]->XO.RUN = CLOCK_XO_RUN_STATUS_Msk;
159 nhw_clkpwr_st.Timer_XO = nsi_hws_get_time(); //we assume the clock is ready in 1 delta
160 nhw_CLOCK_update_master_timer();
161 }
162 }
163
nhw_CLOCK_TASK_XOSTOP(uint inst)164 static void nhw_CLOCK_TASK_XOSTOP(uint inst) {
165 (void) inst;
166 if ((nhw_clkpwr_st.XO_state == Started) || (nhw_clkpwr_st.XO_state == Starting)) {
167 nhw_clkpwr_st.XO_state = Stopping;
168 NRF_CLOCK_regs[0]->XO.RUN = 0;
169 /* Instantaneous stop */
170 nhw_CLOCK_XOTimer_triggered();
171 }
172 }
173
nhw_CLOCK_TASK_PLLSTART(uint inst)174 static void nhw_CLOCK_TASK_PLLSTART(uint inst) {
175 (void) inst;
176 if ((nhw_clkpwr_st.PLL_state == Stopped ) || (nhw_clkpwr_st.PLL_state == Stopping)) {
177 nhw_clkpwr_st.PLL_state = Starting;
178 NRF_CLOCK_regs[0]->PLL.RUN = CLOCK_PLL_RUN_STATUS_Msk;
179 nhw_clkpwr_st.Timer_PLL = nsi_hws_get_time();
180 nhw_CLOCK_update_master_timer();
181 }
182 }
183
nhw_CLOCK_TASK_PLLSTOP(uint inst)184 static void nhw_CLOCK_TASK_PLLSTOP(uint inst) {
185 (void) inst;
186 if ((nhw_clkpwr_st.PLL_state == Started) || (nhw_clkpwr_st.PLL_state == Starting)) {
187 nhw_clkpwr_st.PLL_state = Stopping;
188 NRF_CLOCK_regs[0]->PLL.RUN = 0;
189 /* Instantaneous stop */
190 nhw_CLOCK_PLLTimer_triggered();
191 }
192 }
193
nhw_CLOCK_TASK_LFCLKSTART(uint inst)194 static void nhw_CLOCK_TASK_LFCLKSTART(uint inst) {
195 (void) inst;
196 if ((nhw_clkpwr_st.LFCLK_state == Stopped ) || (nhw_clkpwr_st.LFCLK_state == Stopping)) {
197 nhw_clkpwr_st.LFCLK_state = Starting;
198 NRF_CLOCK_regs[0]->LFCLK.RUN = CLOCK_LFCLK_RUN_STATUS_Msk;
199 NRF_CLOCK_regs[0]->LFCLK.SRCCOPY = NRF_CLOCK_regs[0]->LFCLK.SRC;
200 nhw_clkpwr_st.Timer_LFCLK = nsi_hws_get_time();
201 nhw_CLOCK_update_master_timer();
202 }
203 }
204
nhw_CLOCK_TASK_LFCLKSTOP(uint inst)205 static void nhw_CLOCK_TASK_LFCLKSTOP(uint inst) {
206 (void) inst;
207 if ((nhw_clkpwr_st.LFCLK_state == Started) || (nhw_clkpwr_st.LFCLK_state == Starting)) {
208 nhw_clkpwr_st.LFCLK_state = Stopping;
209 NRF_CLOCK_regs[0]->LFCLK.RUN = 0;
210 /* Instantaneous stop */
211 nhw_CLOCK_LFCLK_triggered();
212 }
213 }
214
nhw_CLOCK_TASK_CAL(uint inst)215 static void nhw_CLOCK_TASK_CAL(uint inst) {
216 (void) inst;
217 if (nhw_clkpwr_st.XO_state != Started) { /* LCOV_EXCL_START */
218 bs_trace_warning_line("%s: Triggered RC oscillator calibration with the HFXO CLK stopped "
219 "(the model does not have a problem with this, but this is against "
220 "the spec)\n", __func__);
221 } /* LCOV_EXCL_STOP */
222
223 if ((nhw_clkpwr_st.CAL_state == Stopped ) || (nhw_clkpwr_st.CAL_state == Stopping)) {
224 nhw_clkpwr_st.CAL_state = Starting;
225 nhw_clkpwr_st.Timer_CAL = nsi_hws_get_time();
226 nhw_CLOCK_update_master_timer();
227 }
228 }
229
nhw_CLOCK_TASK_XOTUNE(uint inst)230 static void nhw_CLOCK_TASK_XOTUNE(uint inst) {
231 (void) inst;
232 if ((nhw_clkpwr_st.XOTUNE_state == Stopped ) || (nhw_clkpwr_st.XOTUNE_state == Stopping)) {
233 nhw_clkpwr_st.XOTUNE_state = Starting;
234 nhw_clkpwr_st.Timer_XOTUNE = nsi_hws_get_time();
235 nhw_CLOCK_update_master_timer();
236 }
237 }
238
nhw_CLOCK_TASK_XOTUNEABORT(uint inst)239 static void nhw_CLOCK_TASK_XOTUNEABORT(uint inst) {
240 (void) inst;
241 /* Deliberately empty by now */
242 }
243
244 NHW_SIDEEFFECTS_INTSET(CLOCK, NRF_CLOCK_regs[0]->, NRF_CLOCK_regs[0]->INTEN)
245 NHW_SIDEEFFECTS_INTCLR(CLOCK, NRF_CLOCK_regs[0]->, NRF_CLOCK_regs[0]->INTEN)
246 NHW_SIDEEFFECTS_INTEN(CLOCK, NRF_CLOCK_regs[0]->, NRF_CLOCK_regs[0]->INTEN)
247
NHW_SIDEEFFECTS_EVENTS(CLOCK)248 NHW_SIDEEFFECTS_EVENTS(CLOCK)
249
250 void nhw_pwrclk_regw_sideeffects_EVENTS_all(uint inst) {
251 nhw_CLOCK_eval_interrupt(inst);
252 }
253
254 #if !defined(CLOCK_PUBLISH_XOSTARTED_ResetValue) /* no PUBLISH registers in MDK */
255 #undef NHW_SIGNAL_EVENT
256 #define NHW_SIGNAL_EVENT(peri, peri_regs, event) \
257 void nhw_##peri##_signal_EVENTS_##event(unsigned int inst) \
258 { \
259 peri_regs EVENTS_##event = 1; \
260 nhw_##peri##_eval_interrupt(inst); \
261 }
262 #endif
263
264 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, XOSTARTED)
265 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, PLLSTARTED)
266 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, LFCLKSTARTED)
267 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, DONE)
268 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, XOTUNED)
269 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, XOTUNEERROR)
270 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, XOTUNEFAILED)
271
272 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, XOSTART)
273 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, XOSTOP)
274 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, PLLSTART)
275 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, PLLSTOP)
276 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, LFCLKSTART)
277 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, LFCLKSTOP)
278 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, CAL)
279 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, XOTUNE)
280 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, XOTUNEABORT)
281
282 #define NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(task) \
283 static void nhw_CLOCK_TASK_##task##_wrap(void *param) { \
284 nhw_CLOCK_TASK_##task((int) param); \
285 } \
286 void nhw_CLOCK_regw_sideeffects_SUBSCRIBE_##task(unsigned int inst) { \
287 static struct nhw_subsc_mem task##_subscribed[NHW_CLKPWR_TOTAL_INST]; \
288 nhw_dppi_common_subscribe_sideeffect(nhw_CLOCK_dppi_map[inst], \
289 NRF_CLOCK_regs[0]->SUBSCRIBE_##task, \
290 &task##_subscribed[inst], \
291 nhw_CLOCK_TASK_##task##_wrap, \
292 (void*) inst); \
293 }
294
NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(XOSTART)295 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(XOSTART)
296 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(XOSTOP)
297 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(PLLSTART)
298 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(PLLSTOP)
299 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(LFCLKSTART)
300 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(LFCLKSTOP)
301 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(CAL)
302 #if defined(CLOCK_SUBSCRIBE_XOTUNE_EN_Msk)
303 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(XOTUNE)
304 #endif
305 #if defined(CLOCK_SUBSCRIBE_XOTUNEABORT_EN_Msk)
306 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(XOTUNEABORT)
307 #endif
308
309 static void nhw_CLOCK_XOTimer_triggered(void) {
310 nhw_clkpwr_st.Timer_XO = TIME_NEVER;
311 nhw_CLOCK_update_master_timer();
312
313 if ( nhw_clkpwr_st.XO_state == Starting ){
314 nhw_clkpwr_st.XO_state = Started;
315
316 NRF_CLOCK_regs[0]->XO.STAT = CLOCK_XO_STAT_STATE_Msk;
317
318 nhw_CLOCK_signal_EVENTS_XOSTARTED(0);
319 nhw_CLOCK_signal_EVENTS_XOTUNED(0);
320
321 } else if ( nhw_clkpwr_st.XO_state == Stopping ){
322 nhw_clkpwr_st.XO_state = Stopped;
323 NRF_CLOCK_regs[0]->XO.STAT = 0;
324 }
325 }
326
nhw_CLOCK_PLLTimer_triggered(void)327 static void nhw_CLOCK_PLLTimer_triggered(void) {
328 nhw_clkpwr_st.Timer_PLL = TIME_NEVER;
329 nhw_CLOCK_update_master_timer();
330
331 if ( nhw_clkpwr_st.PLL_state == Starting ){
332 nhw_clkpwr_st.PLL_state = Started;
333
334 NRF_CLOCK_regs[0]->PLL.STAT = CLOCK_PLL_STAT_STATE_Msk;
335
336 nhw_CLOCK_signal_EVENTS_PLLSTARTED(0);
337
338 } else if ( nhw_clkpwr_st.PLL_state == Stopping ){
339 nhw_clkpwr_st.PLL_state = Stopped;
340 NRF_CLOCK_regs[0]->PLL.STAT = 0;
341 }
342 }
343
nhw_CLOCK_LFCLK_triggered(void)344 static void nhw_CLOCK_LFCLK_triggered(void) {
345 nhw_clkpwr_st.Timer_LFCLK = TIME_NEVER;
346 nhw_CLOCK_update_master_timer();
347
348 if ( nhw_clkpwr_st.LFCLK_state == Starting ){
349 nhw_clkpwr_st.LFCLK_state = Started;
350
351 NRF_CLOCK_regs[0]->LFCLK.STAT = CLOCK_LFCLK_STAT_STATE_Msk
352 | (NRF_CLOCK_regs[0]->LFCLK.SRCCOPY << CLOCK_LFCLK_STAT_SRC_Pos);
353
354 nhw_CLOCK_signal_EVENTS_LFCLKSTARTED(0);
355
356 } else if ( nhw_clkpwr_st.LFCLK_state == Stopping ){
357 nhw_clkpwr_st.LFCLK_state = Stopped;
358 NRF_CLOCK_regs[0]->LFCLK.STAT = 0;
359 }
360 }
361
nhw_CLOCK_CALtimer_triggered(void)362 static void nhw_CLOCK_CALtimer_triggered(void) {
363 nhw_clkpwr_st.CAL_state = Stopped;
364 nhw_clkpwr_st.Timer_CAL = TIME_NEVER;
365 nhw_CLOCK_update_master_timer();
366 nhw_CLOCK_signal_EVENTS_DONE(0);
367 }
368
nhw_CLOCK_XOTUNEtimer_triggered(void)369 static void nhw_CLOCK_XOTUNEtimer_triggered(void) {
370 nhw_clkpwr_st.XOTUNE_state = Stopped;
371 nhw_clkpwr_st.Timer_XOTUNE = TIME_NEVER;
372 nhw_CLOCK_update_master_timer();
373 nhw_CLOCK_signal_EVENTS_XOTUNED(0);
374 }
375
nhw_pwrclk_timer_triggered(void)376 static void nhw_pwrclk_timer_triggered(void) {
377 if (Timer_PWRCLK == nhw_clkpwr_st.Timer_XO) {
378 nhw_CLOCK_XOTimer_triggered();
379 } else if (Timer_PWRCLK == nhw_clkpwr_st.Timer_PLL) {
380 nhw_CLOCK_PLLTimer_triggered();
381 } else if (Timer_PWRCLK == nhw_clkpwr_st.Timer_LFCLK) {
382 nhw_CLOCK_LFCLK_triggered();
383 } else if (Timer_PWRCLK == nhw_clkpwr_st.Timer_CAL) {
384 nhw_CLOCK_CALtimer_triggered();
385 } else if (Timer_PWRCLK == nhw_clkpwr_st.Timer_XOTUNE) {
386 nhw_CLOCK_XOTUNEtimer_triggered();
387 } else { /* LCOV_EXCL_START */
388 bs_trace_error_time_line("%s programming error\n", __func__);
389 } /* LCOV_EXCL_STOP */
390 }
391
392 NSI_HW_EVENT(Timer_PWRCLK, nhw_pwrclk_timer_triggered, 50);
393
394 //TODO: HAL
395