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