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