1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * DPPI - Distributed programmable peripheral interconnect
9  * https://infocenter.nordicsemi.com/topic/ps_nrf5340/dppi.html?cp=4_0_0_6_8
10  *
11  * Notes:
12  *  * Unlike in real HW, if a set of SUBSCRIBE_CHG[n].EN and SUBSCRIBE_CHG[n].DIS
13  *    would be triggered by the same event, and affect the same channels,
14  *    the enables would not have priority over the disables.
15  *    Instead the channels would be enabled and disabled in order of their group
16  *    tasks subscriptions.
17  *    (Changing this behavior is possible with some extra implementation effort)
18  *
19  *  * No SPU controlled security checks are yet implemented:
20  *    * Any peripheral (secure or not) may subscribe and publish to any channel
21  *      (and their events will not be gated irrespectively of the SPU configuration)
22  *    * Channel groups are not bundled as secure/non-secure based
23  *      on the individual channels security
24  *    * Both secure and non-secure bus accesses can read & modify all configuration
25  *      registers (and therefore affect all channels and channel groups)
26  *
27  *  * At this point, if multiple events are sent to the same DPPI at the same "time"
28  *    (same delta cycle) they are not merged. (Changing this behavior is possible:
29  *    Future impl note: A list of pended callbacks could be created,
30  *    and one delta cycle later it could be gone thru)
31  *
32  * Implementation spec:
33  *   This file provides the implementation of the DPPI peripherals,
34  *   and instantiates N of them (and initializes them at startup and frees them on exit),
35  *   as described in the configuration (NHW_config.h)
36  *
37  *   Each DPPI has a configurable number of channels and of channel groups.
38  *   Each DPPI is also connected to a DPPI (for task subscription) as per the configuration.
39  *
40  *   Each DPPI keeps two main structures as its status:
41  *    Its register layout NRF_DPPIC_regs[inst]
42  *    and its status structure nhw_dppi_st[inst]
43  *
44  *   To speed up execution and registration simplicity,
45  *   peripherals task/subscription ports are *not* permanently "plugged" to the DPPI.
46  *   Instead, when a peripheral subscribe register is written, if the subscription is
47  *   enabled, the peripheral is dynamically registered into that DPPI channel.
48  *   This is done by keeping a list of subscribed callbacks in nhw_dppi_st.registry[channel]
49  *   This list will grow dynamically as needed (if many peripherals subscribe to the same channel)
50  *
51  *   When a peripheral publishes an event to a channel, the DPPI will go thru
52  *   all currently subscribed peripherals and call their respective callbacks
53  *   (the respective task handlers)
54  */
55 
56 #include <stdint.h>
57 #include <stdbool.h>
58 #include <string.h>
59 #include "bs_types.h"
60 #include "bs_tracing.h"
61 #include "bs_oswrap.h"
62 #include "nsi_tasks.h"
63 #include "nsi_hws_models_if.h"
64 #include "NHW_common_types.h"
65 #include "NHW_config.h"
66 #include "NHW_DPPI.h"
67 #include "NHW_peri_types.h"
68 
69 #define DPPI_ALLOC_CHUNK_SIZE 4
70 #define SUBSCRIBE_EN_MASK (0x1UL << 31)
71 #define SUBSCRIBE_CHIDX_MASK (0xFFU)
72 
73 struct dppi_registry_el {
74   dppi_callback_t callback;
75   void *param;
76 };
77 
78 typedef void (*dppi_callback_noparam_t)(void);
79 
80 struct dppi_status {
81   NRF_DPPIC_Type *NRF_DPPIC_regs;
82   uint n_ch;  /* Number of channels configured in this DPPI instance */
83   uint n_chg; /* Number of channels' groups configured in this DPPI instance */
84 
85   /* Registry of subscribed peripherals to this DPPI channels*/
86   struct dppi_registry_el **registry; //[n_ch][reg_size]
87   uint *reg_size; //[n_ch] Total allocated size of registry[n_ch]
88   uint *reg_used; //[n_ch] Number of used entries in registry[n_ch]
89 
90   /* DPPI interface as a "normal peripheral" to a DPPI: */
91   uint dppi_map; //To which DPPI instance are this DPPI subscription ports connected to
92   //Which of the subscriptions ports are currently connected, and to which channel:
93   struct nhw_subsc_mem *CHG_EN_subscribed; //[n_chg]
94   struct nhw_subsc_mem *CHG_DIS_subscribed; //[n_chg]
95 
96   uint32_t *shadow_CHG; /*[n_chg] Shadowed/internal version of the CHG register to allow
97                                   ignoring/reverting disabled writes to CHG */
98 };
99 
100 static struct dppi_status nhw_dppi_st[NHW_DPPI_TOTAL_INST];
101 NRF_DPPIC_Type NRF_DPPIC_regs[NHW_DPPI_TOTAL_INST];
102 
103 /*
104  * Initialize all DPPI instances for this SOC
105  */
nhw_dppi_init(void)106 static void nhw_dppi_init(void) {
107   static uint nhw_dppi_dppi_map[NHW_DPPI_TOTAL_INST] = NHW_DPPI_DPPI_MAP;
108   static uint nhw_dppi_n_ch[NHW_DPPI_TOTAL_INST] = NHW_DPPI_N_CH;
109   static uint nhw_dppi_n_chg[NHW_DPPI_TOTAL_INST] = NHW_DPPI_N_CHG;
110 
111   memset(NRF_DPPIC_regs, 0x0, sizeof(NRF_DPPIC_regs));
112 
113   for (int i = 0; i < NHW_DPPI_TOTAL_INST; i ++) {
114     struct dppi_status *el = &nhw_dppi_st[i];
115 
116     el->NRF_DPPIC_regs = &NRF_DPPIC_regs[i];
117     el->n_ch = nhw_dppi_n_ch[i];
118     el->n_chg = nhw_dppi_n_chg[i];
119 
120     int n_ch = nhw_dppi_n_ch[i];
121 
122     el->registry = (struct dppi_registry_el **)bs_calloc(n_ch, sizeof(void*));
123     el->reg_size = (uint*)bs_calloc(n_ch, sizeof(uint));
124     el->reg_used = (uint*)bs_calloc(n_ch, sizeof(uint));
125     /*
126      * Note that each element of registry starts as NULL,
127      * and reg_size as 0, the first time somebody tries to subscribe
128      * to a given channel, the registry will be allocated
129      */
130 
131     int n_chg = nhw_dppi_n_chg[i];
132 
133     el->dppi_map = nhw_dppi_dppi_map[i];
134     el->CHG_EN_subscribed = (struct nhw_subsc_mem*)bs_calloc(n_chg, sizeof(struct nhw_subsc_mem));
135     el->CHG_DIS_subscribed = (struct nhw_subsc_mem*)bs_calloc(n_chg, sizeof(struct nhw_subsc_mem));
136 
137     el->shadow_CHG = (uint32_t*)bs_calloc(n_chg, sizeof(uint32_t));
138   }
139 }
140 
141 NSI_TASK(nhw_dppi_init, HW_INIT, 10);
142 
143 /*
144  * Free all DPPI instances resources before program exit
145  */
nhw_dppi_free(void)146 static void nhw_dppi_free(void)
147 {
148   for (int i = 0; i < NHW_DPPI_TOTAL_INST; i ++) {
149     if (nhw_dppi_st[i].registry != NULL) { /* LCOV_EXCL_BR_LINE */
150       int n_ch = nhw_dppi_st[i].n_ch;
151       for (int n = 0; n < n_ch; n++) {
152         if (nhw_dppi_st[i].registry[n] != NULL) { /* LCOV_EXCL_BR_LINE */
153           free(nhw_dppi_st[i].registry[n]);
154         }
155       }
156       free(nhw_dppi_st[i].registry);
157       nhw_dppi_st[i].registry = NULL;
158     }
159     free(nhw_dppi_st[i].reg_size);
160     nhw_dppi_st[i].reg_size = NULL;
161 
162     free(nhw_dppi_st[i].reg_used);
163     nhw_dppi_st[i].reg_used = NULL;
164 
165     free(nhw_dppi_st[i].CHG_EN_subscribed);
166     nhw_dppi_st[i].CHG_EN_subscribed = NULL;
167 
168     free(nhw_dppi_st[i].CHG_DIS_subscribed);
169     nhw_dppi_st[i].CHG_DIS_subscribed = NULL;
170 
171     free(nhw_dppi_st[i].shadow_CHG);
172     nhw_dppi_st[i].shadow_CHG = NULL;
173   }
174 }
175 
176 NSI_TASK(nhw_dppi_free, ON_EXIT_PRE, 100);
177 
nhw_dppi_check_inst_valid(unsigned int dppi_inst,const char * type)178 static void nhw_dppi_check_inst_valid(unsigned int dppi_inst,
179                                       const char* type)
180 {
181   if (dppi_inst >= NHW_DPPI_TOTAL_INST) { /* LCOV_EXCL_START */
182     bs_trace_error_time_line("%s: Attempted to %s non existent DPPI device (%i>=%i)\n",
183                                   __func__, type, dppi_inst, NHW_DPPI_TOTAL_INST);
184   } /* LCOV_EXCL_STOP */
185 }
186 
nhw_dppi_check_ch_valid(unsigned int dppi_inst,unsigned int ch_n,const char * type)187 static void nhw_dppi_check_ch_valid(unsigned int dppi_inst,
188                                     unsigned int ch_n,
189                                     const char* type)
190 {
191   nhw_dppi_check_inst_valid(dppi_inst, type);
192 
193   struct dppi_status *this = &nhw_dppi_st[dppi_inst];
194   if (this->registry == NULL) { /* LCOV_EXCL_START */
195     bs_trace_error_time_line("%s: Programming error: Attempted to %s DDPI%i "
196                              "which is not initialized or already cleaned up\n",
197                               __func__, type, dppi_inst);
198   }
199   if (ch_n >= this->n_ch) {
200     bs_trace_error_time_line("%s: Attempted to %s non existent channel (%i>=%i) "
201                              "in DDPI%i\n",
202                               __func__, type, ch_n, this->n_ch, dppi_inst);
203   } /* LCOV_EXCL_STOP */
204 }
205 
nhw_dppi_check_chg_valid(unsigned int dppi_inst,unsigned int chg_n,const char * type)206 static void nhw_dppi_check_chg_valid(unsigned int dppi_inst,
207                                      unsigned int chg_n,
208                                      const char* type)
209 {
210   nhw_dppi_check_inst_valid(dppi_inst, type);
211 
212   struct dppi_status *this = &nhw_dppi_st[dppi_inst];
213 
214   if (this->registry == NULL) { /* LCOV_EXCL_START */
215     bs_trace_error_time_line("%s: Programming error: Attempted to %s DDPI%i "
216                              "which is not initialized or already cleaned up\n",
217                               __func__, type, dppi_inst);
218   }
219   if (chg_n >= this->n_chg) {
220     bs_trace_error_time_line("%s: Attempted to %s non existent channel group "
221                              " (%i>=%i) in DDPI%i\n",
222                               __func__, type, chg_n, this->n_chg, dppi_inst);
223   } /* LCOV_EXCL_STOP */
224 }
225 
226 /*
227  * Subscribe a peripheral to a DPPI channel
228  *
229  * dppi_inst   DPPI instance
230  * channel_nbr The channel in that DPPI
231  * callback    The function which will be called when the DPPI event is received
232  * param       An arbitrary void pointer, which will be passed to the callback
233  *
234  * Note if param is DPPI_CB_NO_PARAM, the callback will be called with the signature
235  * void (*dppi_callback_t)(void)
236  *
237  * Note: The pair {callback, param} is used to identify a registration
238  *       Therefore 2 registrations may never use the same pair (it is an error to do so)
239  */
nhw_dppi_channel_subscribe(unsigned int dppi_inst,unsigned int ch_n,dppi_callback_t callback,void * param)240 void nhw_dppi_channel_subscribe(unsigned int dppi_inst,
241                                 unsigned int ch_n,
242                                 dppi_callback_t callback,
243                                 void *param)
244 {
245   nhw_dppi_check_ch_valid(dppi_inst, ch_n, "subscribe to");
246 
247   struct dppi_status *this = &nhw_dppi_st[dppi_inst];
248   struct dppi_registry_el *ch_reg = this->registry[ch_n];
249 
250   for (int i = 0; i < this->reg_used[ch_n]; i++) {
251     if ((ch_reg[i].callback == callback) /* LCOV_EXCL_START */
252         && (ch_reg[i].param == param)) {
253       bs_trace_error_time_line("%s: Programming error: Attempted to subscribe "
254                                "twice to DDPI%i ch %i\n",
255                                 __func__, dppi_inst, ch_n);
256     } /* LCOV_EXCL_STOP */
257   }
258 
259   if (this->reg_used[ch_n] >= this->reg_size[ch_n]) {
260     this->reg_size[ch_n] += DPPI_ALLOC_CHUNK_SIZE;
261     this->registry[ch_n] = bs_realloc(this->registry[ch_n],
262                                       this->reg_size[ch_n] * sizeof(struct dppi_registry_el));
263   }
264   int n = this->reg_used[ch_n];
265   this->registry[ch_n][n].callback = callback;
266   this->registry[ch_n][n].param = param;
267   this->reg_used[ch_n]++;
268 }
269 
nhw_dppi_shift_registration(struct dppi_registry_el * ch_reg,int off,int n)270 static inline void nhw_dppi_shift_registration(struct dppi_registry_el *ch_reg,
271                                                int off,
272                                                int n)
273 {
274   for (int i = off+1; i < n; i++) {
275     ch_reg[i-1].callback = ch_reg[i].callback;
276     ch_reg[i-1].param = ch_reg[i].param;
277   }
278   ch_reg[n-1].callback = NULL;
279   ch_reg[n-1].param = NULL;
280 }
281 
282 /*
283  * Un-subscribe a peripheral from a DPPI channel
284  *
285  * dppi_inst           DPPI instance
286  * channel_nbr         The channel in that DPPI into which we unsubscribe
287  * callback and param: The parameters which were used during the registration
288  *
289  * Note: The pair {callback, param} are used to identify a registration
290  *       Therefore 2 registrations may never use the same pair (it is an error to do so)
291  */
nhw_dppi_channel_unsubscribe(unsigned int dppi_inst,unsigned int ch_n,dppi_callback_t callback,void * param)292 void nhw_dppi_channel_unsubscribe(unsigned int dppi_inst,
293                                   unsigned int ch_n,
294                                   dppi_callback_t callback,
295                                   void *param)
296 {
297   nhw_dppi_check_ch_valid(dppi_inst, ch_n, "unsubscribe from");
298 
299   struct dppi_status *this = &nhw_dppi_st[dppi_inst];
300   struct dppi_registry_el *ch_reg = this->registry[ch_n];
301 
302   for (int i = 0; i < this->reg_used[ch_n]; i++) {
303     if ((ch_reg[i].callback == callback)
304         && (ch_reg[i].param == param)) {
305       nhw_dppi_shift_registration(ch_reg, i, this->reg_used[ch_n]);
306       this->reg_used[ch_n]--;
307       return;
308     }
309   }
310   bs_trace_error_time_line("%s: Programming error: Attempted to unsubscribe but "
311                            "previous subscription not found in DDPI%i ch%i\n",
312                            __func__, dppi_inst, ch_n);
313 }
314 
315 /*
316  * Signal an event to a channel from a peripheral
317  * This will cause all peripherals which subscribed to that channel
318  * to get their callbacks called.
319  *
320  * dppi_inst DPPI instance
321  * ch_n      The channel in which the event is being published
322  */
nhw_dppi_event_signal(uint dppi_inst,uint ch_n)323 void nhw_dppi_event_signal(uint dppi_inst, uint ch_n)
324 {
325   nhw_dppi_check_ch_valid(dppi_inst, ch_n, "send event to");
326   struct dppi_status *this = &nhw_dppi_st[dppi_inst];
327 
328   if ((this->NRF_DPPIC_regs->CHEN & ((uint32_t)0x1 << ch_n)) == 0) {
329     return;
330   }
331 
332   struct dppi_registry_el *ch_reg = this->registry[ch_n];
333 
334   for (int i = 0; i < this->reg_used[ch_n]; i++) {
335     if (ch_reg[i].callback) { /* LCOV_EXCL_BR_LINE */
336       if (ch_reg[i].param != (void*)DPPI_CB_NO_PARAM) {
337         ch_reg[i].callback(ch_reg[i].param);
338       } else {
339         ((dppi_callback_noparam_t)ch_reg[i].callback)();
340       }
341     } else { /* LCOV_EXCL_START */
342       bs_trace_error_time_line("%s: Programming error: Hole in "
343                                " DPPI %i registry, ch%i i=%i\n",
344                                __func__, dppi_inst, ch_n, i);
345     } /* LCOV_EXCL_STOP */
346   }
347 }
348 
nhw_dppi_task_chg_en(uint dppi_inst,uint n)349 static void nhw_dppi_task_chg_en(uint dppi_inst, uint n)
350 {
351   nhw_dppi_check_chg_valid(dppi_inst, n, "triggered TASK_CHGn_EN in");
352 
353   NRF_DPPIC_Type *regs = nhw_dppi_st[dppi_inst].NRF_DPPIC_regs;
354 
355   regs->CHEN |= regs->CHG[n];
356   regs->CHENSET = regs->CHEN;
357 }
358 
nhw_dppi_task_chg_dis(uint dppi_inst,uint n)359 static void nhw_dppi_task_chg_dis(uint dppi_inst, uint n)
360 {
361   nhw_dppi_check_chg_valid(dppi_inst, n, "triggered TASK_CHGn_EN in");
362 
363   NRF_DPPIC_Type *regs = nhw_dppi_st[dppi_inst].NRF_DPPIC_regs;
364 
365   regs->CHEN &= ~regs->CHG[n];
366   regs->CHENSET = regs->CHEN;
367 }
368 
nhw_dppi_regw_sideeffects_TASK_CHGn_EN(uint dppi_inst,uint n)369 void nhw_dppi_regw_sideeffects_TASK_CHGn_EN(uint dppi_inst, uint n)
370 {
371   nhw_dppi_check_chg_valid(dppi_inst, n, "write to TASK_CHGn_EN register in");
372 
373   NRF_DPPIC_Type *regs = nhw_dppi_st[dppi_inst].NRF_DPPIC_regs;
374 
375   if (regs->TASKS_CHG[n].EN) { /* LCOV_EXCL_BR_LINE */
376     regs->TASKS_CHG[n].EN = 0;
377     nhw_dppi_task_chg_en(dppi_inst, n);
378   }
379 }
380 
nhw_dppi_regw_sideeffects_TASK_CHGn_DIS(uint dppi_inst,uint n)381 void nhw_dppi_regw_sideeffects_TASK_CHGn_DIS(uint dppi_inst, uint n)
382 {
383   nhw_dppi_check_chg_valid(dppi_inst, n, "write to TASK_CHGn_DIS register in");
384 
385   NRF_DPPIC_Type *regs = nhw_dppi_st[dppi_inst].NRF_DPPIC_regs;
386 
387   if (regs->TASKS_CHG[n].DIS) { /* LCOV_EXCL_BR_LINE */
388     regs->TASKS_CHG[n].DIS = 0;
389     nhw_dppi_task_chg_dis(dppi_inst, n);
390   }
391 }
392 
nhw_dppi_taskwrap_chg_en(void * param)393 static void nhw_dppi_taskwrap_chg_en(void* param) {
394   unsigned int dppi_inst = (uintptr_t)param >> 16;
395   uint n = (uintptr_t)param & 0xFFFF;
396   nhw_dppi_task_chg_en(dppi_inst, n);
397 }
398 
nhw_dppi_taskwrap_chg_dis(void * param)399 static void nhw_dppi_taskwrap_chg_dis(void* param) {
400   unsigned int dppi_inst = (uintptr_t)param >> 16;
401   uint n = (uintptr_t)param & 0xFFFF;
402   nhw_dppi_task_chg_dis(dppi_inst, n);
403 }
404 
nhw_dppi_regw_sideeffects_SUBSCRIBE_CHG_EN(unsigned int dppi_inst,uint n)405 void nhw_dppi_regw_sideeffects_SUBSCRIBE_CHG_EN(unsigned int dppi_inst, uint n)
406 {
407   nhw_dppi_check_chg_valid(dppi_inst, n, "write to SUBSCRIBE_CHG_EN register in");
408 
409   struct dppi_status *this = &nhw_dppi_st[dppi_inst];
410   NRF_DPPIC_Type *regs = nhw_dppi_st[dppi_inst].NRF_DPPIC_regs;
411 
412   nhw_dppi_common_subscribe_sideeffect(this->dppi_map,
413                                        regs->SUBSCRIBE_CHG[n].EN,
414                                        &this->CHG_EN_subscribed[n],
415                                        nhw_dppi_taskwrap_chg_en,
416                                        (void*)((dppi_inst << 16) + n));
417 }
418 
nhw_dppi_regw_sideeffects_SUBSCRIBE_CHG_DIS(unsigned int dppi_inst,uint n)419 void nhw_dppi_regw_sideeffects_SUBSCRIBE_CHG_DIS(unsigned int dppi_inst, uint n)
420 {
421   nhw_dppi_check_chg_valid(dppi_inst, n, "write to SUBSCRIBE_CHG_DIS register in");
422 
423   struct dppi_status *this = &nhw_dppi_st[dppi_inst];
424   NRF_DPPIC_Type *regs = nhw_dppi_st[dppi_inst].NRF_DPPIC_regs;
425 
426   nhw_dppi_common_subscribe_sideeffect(this->dppi_map,
427                                        regs->SUBSCRIBE_CHG[n].DIS,
428                                        &this->CHG_DIS_subscribed[n],
429                                        nhw_dppi_taskwrap_chg_dis,
430                                        (void*)((dppi_inst << 16) + n));
431 }
432 
433 /**
434  * Update DPPI CHEN mask after a write to CHENSET
435  */
nhw_dppi_regw_sideeffects_CHENSET(uint dppi_inst)436 void nhw_dppi_regw_sideeffects_CHENSET(uint dppi_inst)
437 {
438   nhw_dppi_check_inst_valid(dppi_inst, "access CHENSET register in");
439 
440   NRF_DPPIC_Type *regs = nhw_dppi_st[dppi_inst].NRF_DPPIC_regs;
441 
442   if (regs->CHENSET != 0) { /* LCOV_EXCL_BR_LINE */
443     regs->CHEN |= regs->CHENSET;
444     regs->CHENSET = regs->CHEN;
445   }
446 }
447 
448 /**
449  * Update DPPI CHEN mask after a write to CHENCLR
450  */
nhw_dppi_regw_sideeffects_CHENCLR(uint dppi_inst)451 void nhw_dppi_regw_sideeffects_CHENCLR(uint dppi_inst)
452 {
453   nhw_dppi_check_inst_valid(dppi_inst, "access CHENCLR register in");
454 
455   NRF_DPPIC_Type *regs = nhw_dppi_st[dppi_inst].NRF_DPPIC_regs;
456 
457   if (regs->CHENCLR != 0) { /* LCOV_EXCL_BR_LINE */
458     regs->CHEN &= ~regs->CHENCLR;
459     regs->CHENCLR = 0;
460   }
461 }
462 
nhw_dppi_regw_sideeffects_CHEN(uint dppi_inst)463 void nhw_dppi_regw_sideeffects_CHEN(uint dppi_inst)
464 {
465   nhw_dppi_check_inst_valid(dppi_inst, "access CHENSET register in");
466 
467   NRF_DPPIC_Type *regs = nhw_dppi_st[dppi_inst].NRF_DPPIC_regs;
468   regs->CHENSET = regs->CHEN;
469 }
470 
nhw_dppi_regw_sideeffects_CHGn(uint dppi_inst,uint n)471 void nhw_dppi_regw_sideeffects_CHGn(uint dppi_inst, uint n)
472 {
473   nhw_dppi_check_chg_valid(dppi_inst, n, "access CHENSET register in");
474 
475   struct dppi_status *this = &nhw_dppi_st[dppi_inst];
476   NRF_DPPIC_Type *regs = nhw_dppi_st[dppi_inst].NRF_DPPIC_regs;
477 
478   /*
479    * As per the spec:
480    * Writes to this register are ignored if either SUBSCRIBE_CHG[n].EN
481    * or SUBSCRIBE_CHG[n].DIS is enabled
482    */
483   if ((regs->SUBSCRIBE_CHG[n].EN & SUBSCRIBE_EN_MASK)
484       || (regs->SUBSCRIBE_CHG[n].DIS & SUBSCRIBE_EN_MASK)) {
485     bs_trace_warning_time_line("%s: Ignoring write to DPPI%i CHG%i while one of "
486                                "its subscriptions is enabled (as per the spec)\n",
487                                __func__, dppi_inst, n);
488     regs->CHG[n] = this->shadow_CHG[n];
489     return;
490   }
491 
492   this->shadow_CHG[n] = regs->CHG[n];
493 }
494 
495 /*
496  * NOTE: This is not a DPPI function per se, but a common function
497  * for all peripherals to handle an event publishing
498  *
499  * If the publish_register is enabled,
500  * call nhw_dppi_event_signal() for the configured CHIDX
501  */
nhw_dppi_event_signal_if(uint dppi_inst,uint32_t publish_reg)502 void nhw_dppi_event_signal_if(uint dppi_inst, uint32_t publish_reg) {
503   uint chidx;
504 
505   if ((publish_reg & SUBSCRIBE_EN_MASK) == 0){
506     return;
507   }
508 
509   chidx = publish_reg & SUBSCRIBE_CHIDX_MASK;
510 
511   nhw_dppi_event_signal(dppi_inst, chidx);
512 }
513 
514 /*
515  * NOTE: This is not a DPPI function per se, but a common function
516  * for all peripherals to handle the side-effects of any write to a SUBSCRIBE register
517  *
518  * dppi_inst      Which DPPI the peripheral is connected to
519  * SUBSCRIBE_reg  Value of the SUBSCRIBE_<EVENT> register written by SW
520  * last           Pointer to an static storage in the peripheral, which keeps
521  *                the status of the subscription (is/wast it subscribed,
522  *                and to which channel)
523  *
524  * callback & param: Parameters for nhw_dppi_channel_subscribe()
525  *
526  */
nhw_dppi_common_subscribe_sideeffect(unsigned int dppi_inst,uint32_t SUBSCRIBE_reg,struct nhw_subsc_mem * last,dppi_callback_t callback,void * param)527 void nhw_dppi_common_subscribe_sideeffect(unsigned int dppi_inst,
528                                           uint32_t SUBSCRIBE_reg,
529                                           struct nhw_subsc_mem *last,
530                                           dppi_callback_t callback,
531                                           void *param)
532 {
533   bool new_is_subs = SUBSCRIBE_reg & SUBSCRIBE_EN_MASK;
534   unsigned int new_channel = SUBSCRIBE_reg & SUBSCRIBE_CHIDX_MASK;
535 
536   if ((last->is_subscribed == new_is_subs)
537     && (last->subscribed_ch == new_channel)) {
538     //Nothing has changed
539     return;
540   }
541 
542   if (last->is_subscribed == true) {
543     nhw_dppi_channel_unsubscribe(dppi_inst,
544                                  last->subscribed_ch,
545                                  callback,
546                                  param);
547   }
548   last->is_subscribed = new_is_subs;
549   last->subscribed_ch = new_channel;
550   if (new_is_subs) {
551     nhw_dppi_channel_subscribe(dppi_inst,
552                                new_channel,
553                                callback,
554                                param);
555   }
556 }
557