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
nhw_CLOCK_update_master_timer(void)85 static void nhw_CLOCK_update_master_timer(void) {
86
87 Timer_PWRCLK = TIME_NEVER;
88
89 bs_time_t t1 = BS_MIN(nhw_clkpwr_st.Timer_XO, nhw_clkpwr_st.Timer_PLL);
90 bs_time_t t2 = BS_MIN(nhw_clkpwr_st.Timer_LFCLK, nhw_clkpwr_st.Timer_CAL);
91
92 bs_time_t el_min = BS_MIN(t1, t2);
93 if (el_min < Timer_PWRCLK) {
94 Timer_PWRCLK = el_min;
95 }
96 nsi_hws_find_next_event();
97 }
98
nhw_CLOCK_init(void)99 static void nhw_CLOCK_init(void) {
100 NRF_CLOCK_regs[0] = (NRF_CLOCK_Type *)&NRF_CLKPWR_regs[0];
101 NRF_POWER_regs[0] = (NRF_POWER_Type *)&NRF_CLKPWR_regs[0];
102 NRF_RESET_regs[0] = (NRF_RESET_Type *)&NRF_CLKPWR_regs[0];
103
104 memset(NRF_CLKPWR_regs, 0, sizeof(NRF_CLKPWR_regs));
105
106 nhw_clkpwr_st.Timer_XO = TIME_NEVER;
107 nhw_clkpwr_st.Timer_PLL = TIME_NEVER;
108 nhw_clkpwr_st.Timer_LFCLK = TIME_NEVER;
109 nhw_clkpwr_st.Timer_CAL = TIME_NEVER;
110 nhw_clkpwr_st.Timer_XOTUNE= TIME_NEVER;
111
112 nhw_clkpwr_st.XO_state = Stopped;
113 nhw_clkpwr_st.LFCLK_state = Stopped;
114 nhw_clkpwr_st.PLL_state = Stopped;
115 nhw_clkpwr_st.CAL_state = Stopped;
116 nhw_clkpwr_st.XOTUNE_state= Stopped;
117
118 nhw_CLOCK_update_master_timer();
119 }
120
121 NSI_TASK(nhw_CLOCK_init, HW_INIT, 100);
122
nhw_CLOCK_eval_interrupt(uint inst)123 static void nhw_CLOCK_eval_interrupt(uint inst) {
124 static struct nhw_irq_mapping nhw_CLOCK_irq_map[] = NHW_CLKPWR_INT_MAP;
125 static bool clock_int_line; /* Is the CLOCK currently driving its interrupt line high */
126 bool new_int_line = false;
127
128 NRF_CLOCK_regs[0]->INTPEND = 0;
129
130 #define check_interrupt(x) \
131 if (NRF_CLOCK_regs[0]->EVENTS_ ##x && (NRF_CLOCK_regs[0]->INTEN & CLOCK_INTENSET_## x ##_Msk)){ \
132 new_int_line = 1; \
133 NRF_CLOCK_regs[0]->INTPEND |= CLOCK_INTENSET_## x ##_Msk; \
134 }
135
136 check_interrupt(XOSTARTED)
137 check_interrupt(PLLSTARTED)
138 check_interrupt(LFCLKSTARTED)
139 check_interrupt(DONE)
140 check_interrupt(XOTUNED)
141 check_interrupt(XOTUNEERROR)
142 check_interrupt(XOTUNEFAILED)
143
144 hw_irq_ctrl_toggle_level_irq_line_if(&clock_int_line,
145 new_int_line,
146 &nhw_CLOCK_irq_map[0]);
147 }
148
nhw_CLOCK_TASK_XOSTART(uint inst)149 static void nhw_CLOCK_TASK_XOSTART(uint inst) {
150 if ((nhw_clkpwr_st.XO_state == Stopped ) || (nhw_clkpwr_st.XO_state == Stopping)) {
151 nhw_clkpwr_st.XO_state = Starting;
152 NRF_CLOCK_regs[0]->XO.RUN = CLOCK_XO_RUN_STATUS_Msk;
153 nhw_clkpwr_st.Timer_XO = nsi_hws_get_time(); //we assume the clock is ready in 1 delta
154 nhw_CLOCK_update_master_timer();
155 }
156 }
157
nhw_CLOCK_TASK_XOSTOP(uint inst)158 static void nhw_CLOCK_TASK_XOSTOP(uint inst) {
159 if ((nhw_clkpwr_st.XO_state == Started) || (nhw_clkpwr_st.XO_state == Starting)) {
160 nhw_clkpwr_st.XO_state = Stopping;
161 NRF_CLOCK_regs[0]->XO.RUN = 0;
162 nhw_clkpwr_st.Timer_XO = nsi_hws_get_time(); //we assume the clock is stopped in 1 delta
163 nhw_CLOCK_update_master_timer();
164 }
165 }
166
nhw_CLOCK_TASK_PLLSTART(uint inst)167 static void nhw_CLOCK_TASK_PLLSTART(uint inst) {
168 if ((nhw_clkpwr_st.PLL_state == Stopped ) || (nhw_clkpwr_st.PLL_state == Stopping)) {
169 nhw_clkpwr_st.PLL_state = Starting;
170 NRF_CLOCK_regs[0]->PLL.RUN = CLOCK_PLL_RUN_STATUS_Msk;
171 nhw_clkpwr_st.Timer_PLL = nsi_hws_get_time();
172 nhw_CLOCK_update_master_timer();
173 }
174 }
175
nhw_CLOCK_TASK_PLLSTOP(uint inst)176 static void nhw_CLOCK_TASK_PLLSTOP(uint inst) {
177 if ((nhw_clkpwr_st.PLL_state == Started) || (nhw_clkpwr_st.PLL_state == Starting)) {
178 nhw_clkpwr_st.PLL_state = Stopping;
179 NRF_CLOCK_regs[0]->PLL.RUN = 0;
180 nhw_clkpwr_st.Timer_PLL = nsi_hws_get_time();
181 nhw_CLOCK_update_master_timer();
182 }
183 }
184
nhw_CLOCK_TASK_LFCLKSTART(uint inst)185 static void nhw_CLOCK_TASK_LFCLKSTART(uint inst) {
186 if ((nhw_clkpwr_st.LFCLK_state == Stopped ) || (nhw_clkpwr_st.LFCLK_state == Stopping)) {
187 nhw_clkpwr_st.LFCLK_state = Starting;
188 NRF_CLOCK_regs[0]->LFCLK.RUN = CLOCK_LFCLK_RUN_STATUS_Msk;
189 NRF_CLOCK_regs[0]->LFCLK.SRCCOPY = NRF_CLOCK_regs[0]->LFCLK.SRC;
190 nhw_clkpwr_st.Timer_LFCLK = nsi_hws_get_time();
191 nhw_CLOCK_update_master_timer();
192 }
193 }
194
nhw_CLOCK_TASK_LFCLKSTOP(uint inst)195 static void nhw_CLOCK_TASK_LFCLKSTOP(uint inst) {
196 if ((nhw_clkpwr_st.LFCLK_state == Started) || (nhw_clkpwr_st.LFCLK_state == Starting)) {
197 nhw_clkpwr_st.LFCLK_state = Stopping;
198 NRF_CLOCK_regs[0]->LFCLK.RUN = 0;
199 nhw_clkpwr_st.Timer_LFCLK = nsi_hws_get_time();
200 nhw_CLOCK_update_master_timer();
201 }
202 }
203
nhw_CLOCK_TASK_CAL(uint inst)204 static void nhw_CLOCK_TASK_CAL(uint inst) {
205 if (nhw_clkpwr_st.XO_state != Started) { /* LCOV_EXCL_START */
206 bs_trace_warning_line("%s: Triggered RC oscillator calibration with the HFXO CLK stopped "
207 "(the model does not have a problem with this, but this is against "
208 "the spec)\n", __func__);
209 } /* LCOV_EXCL_STOP */
210
211 if ((nhw_clkpwr_st.CAL_state == Stopped ) || (nhw_clkpwr_st.CAL_state == Stopping)) {
212 nhw_clkpwr_st.CAL_state = Starting;
213 nhw_clkpwr_st.Timer_CAL = nsi_hws_get_time();
214 nhw_CLOCK_update_master_timer();
215 }
216 }
217
nhw_CLOCK_TASK_XOTUNE(uint inst)218 static void nhw_CLOCK_TASK_XOTUNE(uint inst) {
219 if ((nhw_clkpwr_st.XOTUNE_state == Stopped ) || (nhw_clkpwr_st.XOTUNE_state == Stopping)) {
220 nhw_clkpwr_st.XOTUNE_state = Starting;
221 nhw_clkpwr_st.Timer_XOTUNE = nsi_hws_get_time();
222 nhw_CLOCK_update_master_timer();
223 }
224 }
225
nhw_CLOCK_TASK_XOTUNEABORT(uint inst)226 static void nhw_CLOCK_TASK_XOTUNEABORT(uint inst) {
227 /* Deliberately empty by now */
228 }
229
230 NHW_SIDEEFFECTS_INTSET(CLOCK, NRF_CLOCK_regs[0]->, NRF_CLOCK_regs[0]->INTEN)
231 NHW_SIDEEFFECTS_INTCLR(CLOCK, NRF_CLOCK_regs[0]->, NRF_CLOCK_regs[0]->INTEN)
232 NHW_SIDEEFFECTS_INTEN(CLOCK, NRF_CLOCK_regs[0]->, NRF_CLOCK_regs[0]->INTEN)
233
NHW_SIDEEFFECTS_EVENTS(CLOCK)234 NHW_SIDEEFFECTS_EVENTS(CLOCK)
235
236 void nhw_pwrclk_regw_sideeffects_EVENTS_all(uint inst) {
237 nhw_CLOCK_eval_interrupt(inst);
238 }
239
240 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, XOSTARTED)
241 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, PLLSTARTED)
242 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, LFCLKSTARTED)
243 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, DONE)
244 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, XOTUNED)
245 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, XOTUNEERROR)
246 NHW_SIGNAL_EVENT(CLOCK, NRF_CLOCK_regs[0]->, XOTUNEFAILED)
247
248 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, XOSTART)
249 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, XOSTOP)
250 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, PLLSTART)
251 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, PLLSTOP)
252 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, LFCLKSTART)
253 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, LFCLKSTOP)
254 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, CAL)
255 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, XOTUNE)
256 NHW_SIDEEFFECTS_TASKS(CLOCK, NRF_CLOCK_regs[0]->, XOTUNEABORT)
257
258 #define NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(task) \
259 void nhw_CLOCK_regw_sideeffects_SUBSCRIBE_##task(unsigned int inst) { \
260 static struct nhw_subsc_mem task##_subscribed[NHW_CLKPWR_TOTAL_INST]; \
261 nhw_dppi_common_subscribe_sideeffect(nhw_CLOCK_dppi_map[inst], \
262 NRF_CLOCK_regs[0]->SUBSCRIBE_##task, \
263 &task##_subscribed[inst], \
264 (dppi_callback_t)nhw_CLOCK_TASK_##task, \
265 DPPI_CB_NO_PARAM); \
266 }
267
NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(XOSTART)268 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(XOSTART)
269 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(XOSTOP)
270 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(PLLSTART)
271 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(PLLSTOP)
272 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(LFCLKSTART)
273 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(LFCLKSTOP)
274 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(CAL)
275 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(XOTUNE)
276 NHW_CLOCK_SIDEEFFECTS_SUBSCRIBE(XOTUNEABORT)
277
278 static void nhw_CLOCK_XOTimer_triggered(void) {
279 nhw_clkpwr_st.Timer_XO = TIME_NEVER;
280 nhw_CLOCK_update_master_timer();
281
282 if ( nhw_clkpwr_st.XO_state == Starting ){
283 nhw_clkpwr_st.XO_state = Started;
284
285 NRF_CLOCK_regs[0]->XO.STAT = CLOCK_XO_STAT_STATE_Msk;
286
287 //nhw_CLOCK_signal_EVENTS_XOSTARTED(0);
288 nhw_CLOCK_signal_EVENTS_XOTUNED(0);
289
290 } else if ( nhw_clkpwr_st.XO_state == Stopping ){
291 nhw_clkpwr_st.XO_state = Stopped;
292 NRF_CLOCK_regs[0]->XO.STAT = 0;
293 }
294 }
295
nhw_CLOCK_PLLTimer_triggered(void)296 static void nhw_CLOCK_PLLTimer_triggered(void) {
297 nhw_clkpwr_st.Timer_PLL = TIME_NEVER;
298 nhw_CLOCK_update_master_timer();
299
300 if ( nhw_clkpwr_st.PLL_state == Starting ){
301 nhw_clkpwr_st.PLL_state = Started;
302
303 NRF_CLOCK_regs[0]->PLL.STAT = CLOCK_PLL_STAT_STATE_Msk;
304
305 nhw_CLOCK_signal_EVENTS_PLLSTARTED(0);
306
307 } else if ( nhw_clkpwr_st.PLL_state == Stopping ){
308 nhw_clkpwr_st.PLL_state = Stopped;
309 NRF_CLOCK_regs[0]->PLL.STAT = 0;
310 }
311 }
312
nhw_CLOCK_LFCLK_triggered(void)313 static void nhw_CLOCK_LFCLK_triggered(void) {
314 nhw_clkpwr_st.Timer_LFCLK = TIME_NEVER;
315 nhw_CLOCK_update_master_timer();
316
317 if ( nhw_clkpwr_st.LFCLK_state == Starting ){
318 nhw_clkpwr_st.LFCLK_state = Started;
319
320 NRF_CLOCK_regs[0]->LFCLK.STAT = CLOCK_LFCLK_STAT_STATE_Msk
321 | (NRF_CLOCK_regs[0]->LFCLK.SRCCOPY << CLOCK_LFCLK_STAT_SRC_Pos);
322
323 nhw_CLOCK_signal_EVENTS_LFCLKSTARTED(0);
324
325 } else if ( nhw_clkpwr_st.LFCLK_state == Stopping ){
326 nhw_clkpwr_st.LFCLK_state = Stopped;
327 NRF_CLOCK_regs[0]->LFCLK.STAT &= ~CLOCK_LFCLK_STAT_STATE_Msk;
328 }
329 }
330
nhw_CLOCK_CALtimer_triggered(void)331 static void nhw_CLOCK_CALtimer_triggered(void) {
332 nhw_clkpwr_st.CAL_state = Stopped;
333 nhw_clkpwr_st.Timer_CAL = TIME_NEVER;
334 nhw_CLOCK_update_master_timer();
335 nhw_CLOCK_signal_EVENTS_DONE(0);
336 }
337
nhw_CLOCK_XOTUNEtimer_triggered(void)338 static void nhw_CLOCK_XOTUNEtimer_triggered(void) {
339 nhw_clkpwr_st.XOTUNE_state = Stopped;
340 nhw_clkpwr_st.Timer_XOTUNE = TIME_NEVER;
341 nhw_CLOCK_update_master_timer();
342 nhw_CLOCK_signal_EVENTS_XOTUNED(0);
343 }
344
nhw_pwrclk_timer_triggered(void)345 static void nhw_pwrclk_timer_triggered(void) {
346 if (Timer_PWRCLK == nhw_clkpwr_st.Timer_XO) {
347 nhw_CLOCK_XOTimer_triggered();
348 } else if (Timer_PWRCLK == nhw_clkpwr_st.Timer_PLL) {
349 nhw_CLOCK_PLLTimer_triggered();
350 } else if (Timer_PWRCLK == nhw_clkpwr_st.Timer_LFCLK) {
351 nhw_CLOCK_LFCLK_triggered();
352 } else if (Timer_PWRCLK == nhw_clkpwr_st.Timer_CAL) {
353 nhw_CLOCK_CALtimer_triggered();
354 } else if (Timer_PWRCLK == nhw_clkpwr_st.Timer_XOTUNE) {
355 nhw_CLOCK_XOTUNEtimer_triggered();
356 } else { /* LCOV_EXCL_START */
357 bs_trace_error_time_line("%s programming error\n", __func__);
358 } /* LCOV_EXCL_STOP */
359 }
360
361 NSI_HW_EVENT(Timer_PWRCLK, nhw_pwrclk_timer_triggered, 50);
362
363 //TODO: HAL
364