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