1 /*
2 * Copyright (c) 2022 - 2024, Nordic Semiconductor ASA
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <helpers/nrfx_gppi.h>
35
36 #if NRFX_CHECK(NRFX_DPPI_ENABLED) && defined(DPPIC_COUNT) && (DPPIC_COUNT > 1)
37
38 #include <string.h>
39 #include <soc/interconnect/apb/nrfx_interconnect_apb.h>
40 #include <soc/interconnect/ipct/nrfx_interconnect_ipct.h>
41 #include <hal/nrf_ppib.h>
42 #include <helpers/nrfx_flag32_allocator.h>
43
44 #define CHANNEL_INVALID UINT8_MAX
45
46 #define NUMBER_OF_VIRTUAL_CHANNELS NRFX_GPPI_PROG_APP_CHANNELS_NUM
47 #define VIRTUAL_CHANNELS_MASK NRFX_GPPI_PROG_APP_CHANNELS_MASK
48
49 typedef struct
50 {
51 nrfx_interconnect_apb_t const * p_src_apb;
52 nrfx_interconnect_apb_t const * p_dst_apb;
53 uint8_t dppi_channel;
54 uint8_t local_dppi_channel;
55 uint8_t ipct_channel;
56 uint8_t local_ipct_channel;
57 } nrfx_gppi_channels_path_t;
58
59 static nrfx_gppi_channels_path_t channels_path[NUMBER_OF_VIRTUAL_CHANNELS];
60 static nrfx_atomic_t m_virtual_channels = VIRTUAL_CHANNELS_MASK;
61
path_cleanup(nrfx_gppi_channels_path_t * p_path)62 static void path_cleanup(nrfx_gppi_channels_path_t * p_path)
63 {
64 NRFX_ASSERT(p_path);
65 memset(p_path, 0, sizeof(nrfx_gppi_channels_path_t));
66 p_path->dppi_channel = CHANNEL_INVALID;
67 p_path->local_dppi_channel = CHANNEL_INVALID;
68 p_path->ipct_channel = CHANNEL_INVALID;
69 }
70
channel_free(nrfx_atomic_t * p_allocated_channels,uint8_t channel)71 static nrfx_err_t channel_free(nrfx_atomic_t * p_allocated_channels, uint8_t channel)
72 {
73 NRFX_ASSERT(p_allocated_channels);
74 if (channel == CHANNEL_INVALID)
75 {
76 return NRFX_ERROR_INVALID_PARAM;
77 }
78 return nrfx_flag32_free(p_allocated_channels, channel);
79 }
80
channel_allocate(nrfx_atomic_t * p_channels_available,uint8_t * p_channel,uint32_t mask)81 static nrfx_err_t channel_allocate(nrfx_atomic_t * p_channels_available,
82 uint8_t * p_channel,
83 uint32_t mask)
84 {
85 NRFX_ASSERT(p_channel);
86 uint32_t chan_avail;
87 uint32_t chan_avail_masked;
88 uint8_t chan_to_alloc;
89 uint32_t prev_mask;
90
91 do {
92 chan_avail = *p_channels_available;
93 chan_avail_masked = chan_avail & mask;
94 if (chan_avail_masked == 0)
95 {
96 return NRFX_ERROR_NO_MEM;
97 }
98 else
99 {
100 chan_to_alloc = (uint8_t)(31UL - NRF_CLZ(chan_avail_masked));
101 }
102
103 prev_mask = NRFX_ATOMIC_FETCH_AND(p_channels_available,
104 ~NRFX_BIT(chan_to_alloc));
105 } while (prev_mask == *p_channels_available);
106 *p_channel = chan_to_alloc;
107 return NRFX_SUCCESS;
108 }
109
110 /* The main connection is needed when connecting two APBs from Global Domain.
111 In addition no of them is main APB. In case of Haltium microcontrollers the main APB
112 is APB32. */
is_main_connection_needed(nrfx_interconnect_apb_t const * p_src_apb,nrfx_interconnect_apb_t const * p_dst_apb)113 static bool is_main_connection_needed(nrfx_interconnect_apb_t const * p_src_apb,
114 nrfx_interconnect_apb_t const * p_dst_apb)
115 {
116 if (nrfx_interconnect_apb_domain_get(p_src_apb) == NRF_DOMAIN_GLOBAL ||
117 nrfx_interconnect_apb_domain_get(p_dst_apb) == NRF_DOMAIN_GLOBAL)
118 {
119 return (p_src_apb != nrfx_interconnect_apb_main_get() &&
120 p_dst_apb != nrfx_interconnect_apb_main_get() &&
121 p_src_apb != p_dst_apb);
122 }
123 return false;
124 }
125
126 /* This function removes the direct connection between two APBs via PPIB bridge. */
apb_connection_remove(nrfx_interconnect_apb_t const * p_src_apb,nrfx_interconnect_apb_t const * p_dst_apb,uint8_t ppib_channel)127 static void apb_connection_remove(nrfx_interconnect_apb_t const * p_src_apb,
128 nrfx_interconnect_apb_t const * p_dst_apb,
129 uint8_t ppib_channel)
130 {
131 NRFX_ASSERT(p_src_apb != p_dst_apb);
132 NRFX_ASSERT(p_src_apb);
133 NRFX_ASSERT(p_dst_apb);
134 NRFX_ASSERT(nrfx_interconnect_apb_domain_get(p_src_apb) ==
135 nrfx_interconnect_apb_domain_get(p_dst_apb));
136 NRFX_ASSERT(nrfx_interconnect_apb_domain_get(p_src_apb) == NRF_DOMAIN);
137 // Inside local domain PPIB connection should be cleared here.
138 nrf_ppib_subscribe_clear(p_src_apb->p_ppib,
139 nrf_ppib_send_task_get(ppib_channel));
140 nrf_ppib_publish_clear(p_dst_apb->p_ppib,
141 nrf_ppib_receive_event_get(ppib_channel));
142 }
143
144 /* This function connects directly two APBs via PPIB bridge. */
apb_connection_create(nrfx_interconnect_apb_t const * p_src_apb,nrfx_interconnect_apb_t const * p_dst_apb,uint8_t ppib_channel)145 static void apb_connection_create(nrfx_interconnect_apb_t const * p_src_apb,
146 nrfx_interconnect_apb_t const * p_dst_apb,
147 uint8_t ppib_channel)
148 {
149 NRFX_ASSERT(p_src_apb != p_dst_apb);
150 NRFX_ASSERT(p_src_apb);
151 NRFX_ASSERT(p_dst_apb);
152 NRFX_ASSERT(nrfx_interconnect_apb_domain_get(p_src_apb) ==
153 nrfx_interconnect_apb_domain_get(p_dst_apb));
154 NRFX_ASSERT(nrfx_interconnect_apb_domain_get(p_src_apb) == NRF_DOMAIN);
155 // Inside local domain PPIB connection should be set here.
156 nrf_ppib_subscribe_set(p_src_apb->p_ppib,
157 nrf_ppib_send_task_get(ppib_channel),
158 ppib_channel);
159 nrf_ppib_publish_set(p_dst_apb->p_ppib,
160 nrf_ppib_receive_event_get(ppib_channel),
161 ppib_channel);
162 }
163
164 /* This function removes direct connection between two domains via IPCT.
165 It must be called before all local connections are removed
166 (Before all `local_connection_remove()` function calls). */
ipct_connection_remove(nrfx_interconnect_ipct_t const * p_src_ipct,nrfx_interconnect_ipct_t const * p_dst_ipct,nrfx_gppi_channels_path_t * p_path)167 static nrfx_err_t ipct_connection_remove(nrfx_interconnect_ipct_t const * p_src_ipct,
168 nrfx_interconnect_ipct_t const * p_dst_ipct,
169 nrfx_gppi_channels_path_t * p_path)
170 {
171 NRFX_ASSERT(p_src_ipct);
172 NRFX_ASSERT(p_dst_ipct);
173 NRFX_ASSERT(p_dst_ipct != p_src_ipct);
174 NRFX_ASSERT(p_path);
175 NRFX_ASSERT(nrfx_interconnect_ipct_domain_get(p_src_ipct) == NRF_DOMAIN_GLOBAL ||
176 nrfx_interconnect_ipct_domain_get(p_dst_ipct) == NRF_DOMAIN_GLOBAL);
177
178 nrfx_err_t err_code;
179 uint8_t src_ipct_chan = nrfx_interconnect_ipct_domain_get(p_src_ipct) == NRF_DOMAIN_GLOBAL ?
180 p_path->ipct_channel :
181 p_path->local_ipct_channel;
182 uint8_t dst_ipct_chan = nrfx_interconnect_ipct_domain_get(p_dst_ipct) == NRF_DOMAIN_GLOBAL ?
183 p_path->ipct_channel :
184 p_path->local_ipct_channel;
185
186 err_code = channel_free(p_src_ipct->p_ipct_channels, src_ipct_chan);
187 if (err_code == NRFX_SUCCESS)
188 {
189 /* Source channel is either already freed or it is not permitted by the configuration. */
190 err_code = channel_free(p_dst_ipct->p_ipct_channels, dst_ipct_chan);
191 if (err_code == NRFX_SUCCESS)
192 {
193 /* Clearing both IPCT configurations. */
194 nrf_ipct_shorts_disable(p_src_ipct->p_ipct, NRFX_BIT(src_ipct_chan));
195 nrf_ipct_shorts_disable(p_dst_ipct->p_ipct, NRFX_BIT(dst_ipct_chan));
196 nrf_ipct_subscribe_clear(p_src_ipct->p_ipct, nrf_ipct_send_task_get(src_ipct_chan));
197 nrf_ipct_publish_clear(p_dst_ipct->p_ipct, nrf_ipct_receive_event_get(dst_ipct_chan));
198 }
199 }
200 return err_code;
201 }
202
203 /* Enable or disable all channels for all involved DPPIC peripherals. */
dppic_channel_set(uint8_t chan,bool enable)204 static void dppic_channel_set(uint8_t chan, bool enable)
205 {
206 nrfx_gppi_channels_path_t * p_path = &channels_path[chan];
207 nrfx_interconnect_apb_t const * p_src_apb = p_path->p_src_apb;
208 nrfx_interconnect_apb_t const * p_dst_apb = p_path->p_dst_apb;
209 uint8_t src_dppi_chan = nrfx_interconnect_apb_domain_get(p_src_apb) == NRF_DOMAIN_GLOBAL ?
210 p_path->dppi_channel :
211 p_path->local_dppi_channel;
212 uint8_t dst_dppi_chan = nrfx_interconnect_apb_domain_get(p_dst_apb) == NRF_DOMAIN_GLOBAL ?
213 p_path->dppi_channel :
214 p_path->local_dppi_channel;
215
216 NRFX_ASSERT(nrfx_flag32_is_allocated(m_virtual_channels, chan));
217 NRFX_ASSERT(p_src_apb);
218 NRFX_ASSERT(p_dst_apb);
219 nrfy_dppi_channels_set(p_src_apb->p_dppi, NRFX_BIT(src_dppi_chan), enable);
220 nrfy_dppi_channels_set(p_dst_apb->p_dppi, NRFX_BIT(dst_dppi_chan), enable);
221 if (is_main_connection_needed(p_src_apb, p_dst_apb))
222 {
223 nrfy_dppi_channels_set(nrfx_interconnect_apb_main_get()->p_dppi,
224 NRFX_BIT(p_path->dppi_channel),
225 enable);
226 }
227 if (nrfx_interconnect_apb_domain_get(p_src_apb) != nrfx_interconnect_apb_domain_get(p_dst_apb))
228 {
229 // Remove also DPPICn channels related with IPCT peripherals if needed.
230 nrfx_interconnect_ipct_t const * p_src_ipct = nrfx_interconnect_ipct_get(p_src_apb);
231 nrfx_interconnect_ipct_t const * p_dst_ipct = nrfx_interconnect_ipct_get(p_dst_apb);
232 nrfx_interconnect_apb_t const * p_src_ipct_apb =
233 (nrfx_interconnect_apb_get((uint32_t)p_src_ipct->p_ipct));
234 nrfx_interconnect_apb_t const * p_dst_ipct_apb =
235 (nrfx_interconnect_apb_get((uint32_t)p_dst_ipct->p_ipct));
236
237 NRFX_ASSERT(p_src_ipct && p_src_ipct_apb);
238 NRFX_ASSERT(p_dst_ipct && p_dst_ipct_apb);
239 nrfy_dppi_channels_set(p_src_ipct_apb->p_dppi,
240 NRFX_BIT(src_dppi_chan),
241 enable);
242 nrfy_dppi_channels_set(p_dst_ipct_apb->p_dppi,
243 NRFX_BIT(dst_dppi_chan),
244 enable);
245 }
246 }
247
248 /* This function creates direct connection between two domains via IPCT.
249 It must be called after all local connections are created
250 (After all `local_connection_create()` function calls). */
ipct_connection_create(nrfx_interconnect_ipct_t const * p_src_ipct,nrfx_interconnect_ipct_t const * p_dst_ipct,nrfx_gppi_channels_path_t * p_path)251 static nrfx_err_t ipct_connection_create(nrfx_interconnect_ipct_t const * p_src_ipct,
252 nrfx_interconnect_ipct_t const * p_dst_ipct,
253 nrfx_gppi_channels_path_t * p_path)
254 {
255 NRFX_ASSERT(p_src_ipct);
256 NRFX_ASSERT(p_dst_ipct);
257 NRFX_ASSERT(p_dst_ipct != p_src_ipct);
258 NRFX_ASSERT(p_path);
259 NRFX_ASSERT(nrfx_interconnect_ipct_domain_get(p_src_ipct) == NRF_DOMAIN_GLOBAL ||
260 nrfx_interconnect_ipct_domain_get(p_dst_ipct) == NRF_DOMAIN_GLOBAL);
261
262 nrfx_err_t err_code;
263 uint8_t src_dppi_channel;
264 uint8_t dst_dppi_channel;
265 uint8_t * src_ipct_channel;
266 uint8_t * dst_ipct_channel;
267 uint32_t src_chan_mask;
268 uint32_t dst_chan_mask;
269 nrfx_interconnect_apb_t const * p_src_apb =
270 nrfx_interconnect_apb_get((uint32_t)p_src_ipct->p_ipct);
271 nrfx_interconnect_apb_t const * p_dst_apb =
272 nrfx_interconnect_apb_get((uint32_t)p_dst_ipct->p_ipct);
273
274 if (nrfx_interconnect_apb_domain_get(p_src_apb) == NRF_DOMAIN_GLOBAL)
275 {
276 /* Connetion from Global Domain (src) to Local Domain (dst). */
277 src_dppi_channel = p_path->dppi_channel;
278 src_ipct_channel = &p_path->ipct_channel;
279 dst_dppi_channel = p_path->local_dppi_channel;
280 dst_ipct_channel = &p_path->local_ipct_channel;
281 }
282 else if (nrfx_interconnect_apb_domain_get(p_dst_apb) == NRF_DOMAIN_GLOBAL)
283 {
284 /* Connetion from Local Domain (src) to Global Domain (dst). */
285 src_dppi_channel = p_path->local_dppi_channel;
286 src_ipct_channel = &p_path->local_ipct_channel;
287 dst_dppi_channel = p_path->dppi_channel;
288 dst_ipct_channel = &p_path->ipct_channel;
289 }
290 else
291 {
292 return NRFX_ERROR_INVALID_PARAM;
293 }
294
295 src_chan_mask = *p_src_ipct->p_ipct_channels & p_src_ipct->ipct_pub_channels_mask;
296 dst_chan_mask = *p_dst_ipct->p_ipct_channels & p_dst_ipct->ipct_sub_channels_mask;
297 err_code = channel_allocate(p_src_ipct->p_ipct_channels, src_ipct_channel, src_chan_mask);
298 if (err_code == NRFX_SUCCESS)
299 {
300 /* No more IPCT channels available for source domain. */
301 err_code = channel_allocate(p_dst_ipct->p_ipct_channels, dst_ipct_channel, dst_chan_mask);
302 if (err_code == NRFX_SUCCESS)
303 {
304 /* Setting up both IPCT configurations. */
305 nrf_ipct_shorts_enable(p_src_ipct->p_ipct, NRFX_BIT(*src_ipct_channel));
306 nrf_ipct_shorts_enable(p_dst_ipct->p_ipct, NRFX_BIT(*dst_ipct_channel));
307 nrf_ipct_subscribe_set(p_src_ipct->p_ipct,
308 nrf_ipct_send_task_get(*src_ipct_channel),
309 src_dppi_channel);
310 nrf_ipct_publish_set(p_dst_ipct->p_ipct,
311 nrf_ipct_receive_event_get(*dst_ipct_channel),
312 dst_dppi_channel);
313 }
314 else
315 {
316 /* No more IPCT channels available for destination domain.
317 Then We can free previously allocated channel for source domain. */
318 (void)channel_free(p_src_ipct->p_ipct_channels, *src_ipct_channel);
319 }
320 }
321 return err_code;
322 }
323
324 /* This function creates connection between two APBs inside one domain via DPPIC
325 (and PPIB if needed).
326 It must be called before IPCT connection is created
327 (Before `ipct_connection_create()` function call). */
local_connection_create(nrfx_interconnect_apb_t const * p_src_apb,nrfx_interconnect_apb_t const * p_dst_apb,uint8_t * dppi_channel)328 static nrfx_err_t local_connection_create(nrfx_interconnect_apb_t const * p_src_apb,
329 nrfx_interconnect_apb_t const * p_dst_apb,
330 uint8_t * dppi_channel)
331 {
332 nrfx_err_t err_code;
333 uint8_t reserved_src_channel = CHANNEL_INVALID;
334 uint8_t reserved_dst_channel = CHANNEL_INVALID;
335 bool use_main_apb_interconnect = false;
336 uint32_t chan_mask;
337
338 NRFX_ASSERT(p_src_apb);
339 NRFX_ASSERT(p_dst_apb);
340 if (p_src_apb == p_dst_apb)
341 {
342 /* Creating connection whithin one APB. */
343 chan_mask = (*p_src_apb->p_dppi_channels &
344 (p_src_apb->dppi_pub_channels_mask | p_dst_apb->dppi_sub_channels_mask));
345 err_code = channel_allocate(p_src_apb->p_dppi_channels, dppi_channel, chan_mask);
346 }
347 else
348 {
349 /* Creating connection between two different APBs. */
350 NRFX_CRITICAL_SECTION_ENTER();
351 chan_mask = (*p_src_apb->p_dppi_channels & p_src_apb->dppi_pub_channels_mask) &
352 (*p_dst_apb->p_dppi_channels & p_dst_apb->dppi_sub_channels_mask);
353 if (is_main_connection_needed(p_src_apb, p_dst_apb))
354 {
355 /* The path requires to go throught the main APB*/
356 use_main_apb_interconnect = true;
357 chan_mask &= (nrfx_interconnect_apb_main_get()->dppi_pub_channels_mask &
358 nrfx_interconnect_apb_main_get()->dppi_sub_channels_mask &
359 (uint32_t)(*nrfx_interconnect_apb_main_get()->p_dppi_channels));
360 }
361
362 /* Allocating same channel for all involved DPPICs. */
363 err_code = channel_allocate(p_src_apb->p_dppi_channels, dppi_channel, chan_mask);
364 if (err_code == NRFX_SUCCESS)
365 {
366 reserved_src_channel = *dppi_channel;
367 err_code = channel_allocate(p_dst_apb->p_dppi_channels, dppi_channel, chan_mask);
368 if (err_code == NRFX_SUCCESS)
369 {
370 reserved_dst_channel = *dppi_channel;
371 if (use_main_apb_interconnect)
372 {
373 *dppi_channel = CHANNEL_INVALID;
374 err_code = channel_allocate(
375 nrfx_interconnect_apb_main_get()->p_dppi_channels,
376 dppi_channel,
377 chan_mask);
378 }
379 }
380 }
381
382 if (err_code != NRFX_SUCCESS)
383 {
384 /* For at least one of involved DPPICs there was no channels available. */
385 (void)channel_free(p_src_apb->p_dppi_channels, reserved_src_channel);
386 (void)channel_free(p_dst_apb->p_dppi_channels, reserved_dst_channel);
387 (void)channel_free(nrfx_interconnect_apb_main_get()->p_dppi_channels, *dppi_channel);
388 }
389 else if (nrfx_interconnect_apb_domain_get(p_src_apb) == NRF_DOMAIN)
390 {
391 /* Inside our domain we are allowed to configure APB connection by ourself.
392 (For Global Domain it is done by Secure Deomain). */
393 apb_connection_create(p_src_apb, p_dst_apb, *dppi_channel);
394 }
395 NRFX_CRITICAL_SECTION_EXIT();
396 }
397 return err_code;
398 }
399
400 /* This function removes connection between two APBs inside one domain.
401 It must be called after IPCT connection is removed
402 (After `ipct_connection_remove()` function call). */
local_connection_remove(nrfx_interconnect_apb_t const * p_src_apb,nrfx_interconnect_apb_t const * p_dst_apb,uint8_t dppi_channel)403 static nrfx_err_t local_connection_remove(nrfx_interconnect_apb_t const * p_src_apb,
404 nrfx_interconnect_apb_t const * p_dst_apb,
405 uint8_t dppi_channel)
406 {
407 nrfx_err_t err_code;
408
409 NRFX_ASSERT(dppi_channel != CHANNEL_INVALID);
410 NRFX_ASSERT(p_src_apb);
411 NRFX_ASSERT(p_dst_apb);
412 NRFX_ASSERT(nrfx_interconnect_apb_domain_get(p_src_apb) ==
413 nrfx_interconnect_apb_domain_get(p_dst_apb));
414 if (p_src_apb == p_dst_apb)
415 {
416 /* Removing connection within one APB. */
417 err_code = channel_free(p_src_apb->p_dppi_channels, dppi_channel);
418 if (err_code != NRFX_SUCCESS)
419 {
420 return err_code;
421 }
422 }
423 else
424 {
425 /* Removing connection between two APBs. */
426 NRFX_CRITICAL_SECTION_ENTER();
427 err_code = channel_free(p_src_apb->p_dppi_channels, dppi_channel);
428 if (err_code == NRFX_SUCCESS)
429 {
430 err_code = channel_free(p_dst_apb->p_dppi_channels, dppi_channel);
431 if (err_code == NRFX_SUCCESS)
432 {
433 if (is_main_connection_needed(p_src_apb, p_dst_apb))
434 {
435 /* The path required to go throught the main APB*/
436 err_code = channel_free(nrfx_interconnect_apb_main_get()->p_dppi_channels,
437 dppi_channel);
438 }
439 }
440 }
441 NRFX_CRITICAL_SECTION_EXIT();
442 }
443
444 if (err_code != NRFX_SUCCESS)
445 {
446 return err_code;
447 }
448 if (nrfx_interconnect_apb_domain_get(p_src_apb) == NRF_DOMAIN && p_src_apb != p_dst_apb)
449 {
450 /* Inside our domain we are allowed to configure APB connection by ourself. */
451 apb_connection_remove(p_src_apb, p_dst_apb, dppi_channel);
452 }
453 return err_code;
454 }
455
nrfx_gppi_channel_alloc(uint8_t * p_channel)456 nrfx_err_t nrfx_gppi_channel_alloc(uint8_t * p_channel)
457 {
458 nrfx_err_t err;
459 nrfx_gppi_channels_path_t * chan;
460
461 err = nrfx_flag32_alloc(&m_virtual_channels, p_channel);
462 if (err == NRFX_SUCCESS)
463 {
464 chan = &channels_path[*p_channel];
465 path_cleanup(chan);
466 }
467 return err;
468 }
469
nrfx_gppi_event_endpoint_setup(uint8_t channel,uint32_t eep)470 void nrfx_gppi_event_endpoint_setup(uint8_t channel, uint32_t eep)
471 {
472 (void)channel;
473 (void)eep;
474 // `tep` is also needed to decide whether `main_apb` is to be used.
475 NRFX_ASSERT(false);
476 }
477
nrfx_gppi_task_endpoint_setup(uint8_t channel,uint32_t tep)478 void nrfx_gppi_task_endpoint_setup(uint8_t channel, uint32_t tep)
479 {
480 (void)channel;
481 (void)tep;
482 // `eep` is also needed to decide whether `main_apb` is to be used.
483 NRFX_ASSERT(false);
484 }
485
nrfx_gppi_event_endpoint_clear(uint8_t channel,uint32_t eep)486 void nrfx_gppi_event_endpoint_clear(uint8_t channel, uint32_t eep)
487 {
488 (void)channel;
489 (void)eep;
490 // `tep` is also needed to decide whether `main_apb` is to be used.
491 NRFX_ASSERT(false);
492 }
493
nrfx_gppi_task_endpoint_clear(uint8_t channel,uint32_t tep)494 void nrfx_gppi_task_endpoint_clear(uint8_t channel, uint32_t tep)
495 {
496 (void)channel;
497 (void)tep;
498 // `eep` is also needed to decide whether `main_apb` is to be used.
499 NRFX_ASSERT(false);
500 }
501
nrfx_gppi_fork_endpoint_setup(uint8_t channel,uint32_t fork_tep)502 void nrfx_gppi_fork_endpoint_setup(uint8_t channel, uint32_t fork_tep)
503 {
504 NRFX_ASSERT(fork_tep);
505 NRFX_ASSERT(nrfx_flag32_is_allocated(m_virtual_channels, channel));
506 nrfx_interconnect_apb_t const * p_apb = nrfx_interconnect_apb_get(fork_tep);
507 nrfx_gppi_channels_path_t * p_path = &channels_path[channel];
508 uint8_t dppi_chan = nrfx_interconnect_apb_domain_get(p_apb) == NRF_DOMAIN_GLOBAL ?
509 p_path->dppi_channel :
510 p_path->local_dppi_channel;
511 NRFX_ASSERT(p_apb);
512 // The endpoint must belong to one of used APB in existing connection.
513 if ((p_path->p_dst_apb != p_apb) && (p_path->p_src_apb != p_apb))
514 {
515 if (!is_main_connection_needed(p_path->p_src_apb, p_path->p_dst_apb) ||
516 p_apb != nrfx_interconnect_apb_main_get())
517 {
518 NRFX_ASSERT(false);
519 }
520 }
521 NRF_DPPI_ENDPOINT_SETUP(fork_tep, dppi_chan);
522 }
523
nrfx_gppi_fork_endpoint_clear(uint8_t channel,uint32_t fork_tep)524 void nrfx_gppi_fork_endpoint_clear(uint8_t channel, uint32_t fork_tep)
525 {
526 NRFX_ASSERT(fork_tep);
527 NRFX_ASSERT(nrfx_flag32_is_allocated(m_virtual_channels, channel));
528 nrfx_interconnect_apb_t const * p_apb = nrfx_interconnect_apb_get(fork_tep);
529 nrfx_gppi_channels_path_t * p_path = &channels_path[channel];
530
531 NRFX_ASSERT(p_apb);
532 // The endpoint must belong to one of used APB in existing connection.
533 if ((p_path->p_dst_apb != p_apb) && (p_path->p_src_apb != p_apb))
534 {
535 if (!is_main_connection_needed(p_path->p_src_apb, p_path->p_dst_apb) ||
536 p_apb != nrfx_interconnect_apb_main_get())
537 {
538 NRFX_ASSERT(false);
539 }
540 }
541 NRF_DPPI_ENDPOINT_CLEAR(fork_tep);
542 }
543
nrfx_gppi_channel_endpoints_setup(uint8_t channel,uint32_t eep,uint32_t tep)544 void nrfx_gppi_channel_endpoints_setup(uint8_t channel, uint32_t eep, uint32_t tep)
545 {
546 NRFX_ASSERT(tep);
547 NRFX_ASSERT(eep);
548
549 nrfx_err_t err_code;
550 nrfx_interconnect_apb_t const * p_src_apb = (nrfx_interconnect_apb_get(eep));
551 nrfx_interconnect_apb_t const * p_dst_apb = (nrfx_interconnect_apb_get(tep));
552 nrfx_gppi_channels_path_t * p_path = &channels_path[channel];
553
554 NRFX_ASSERT(p_src_apb);
555 NRFX_ASSERT(p_dst_apb);
556
557 uint8_t * src_dppi_chan = nrfx_interconnect_apb_domain_get(p_src_apb) == NRF_DOMAIN_GLOBAL ?
558 &p_path->dppi_channel :
559 &p_path->local_dppi_channel;
560 uint8_t * dst_dppi_chan = nrfx_interconnect_apb_domain_get(p_dst_apb) == NRF_DOMAIN_GLOBAL ?
561 &p_path->dppi_channel :
562 &p_path->local_dppi_channel;
563
564 if (nrfx_interconnect_apb_domain_get(p_src_apb) == nrfx_interconnect_apb_domain_get(p_dst_apb))
565 {
566 /* Endpoints belongs to the same domain - one local connection needed. */
567 NRFX_ASSERT(src_dppi_chan == dst_dppi_chan);
568 err_code = local_connection_create(p_src_apb, p_dst_apb, src_dppi_chan);
569 if (err_code != NRFX_SUCCESS)
570 {
571 NRFX_ASSERT(false);
572 (void)local_connection_remove(p_src_apb, p_dst_apb, *src_dppi_chan);
573 }
574 }
575 else
576 {
577 /* Endpoints in different domains - two local connections and one IPCT connection needed. */
578 nrfx_interconnect_ipct_t const * p_src_ipct = nrfx_interconnect_ipct_get(p_src_apb);
579 nrfx_interconnect_ipct_t const * p_dst_ipct = nrfx_interconnect_ipct_get(p_dst_apb);
580 nrfx_interconnect_apb_t const * p_src_ipct_apb =
581 (nrfx_interconnect_apb_get((uint32_t)p_src_ipct->p_ipct));
582 nrfx_interconnect_apb_t const * p_dst_ipct_apb =
583 (nrfx_interconnect_apb_get((uint32_t)p_dst_ipct->p_ipct));
584 NRFX_ASSERT(src_dppi_chan != dst_dppi_chan);
585
586 /* Creating local connection from source to IPCT peripheral inside the first domain. */
587 err_code = local_connection_create(p_src_apb, p_src_ipct_apb, src_dppi_chan);
588 NRFX_ASSERT(err_code == NRFX_SUCCESS);
589 if (err_code == NRFX_SUCCESS)
590 {
591 /* Creating local connection from IPCT peripheral to destination inside the second domain. */
592 err_code = local_connection_create(p_dst_ipct_apb, p_dst_apb, dst_dppi_chan);
593 NRFX_ASSERT(err_code == NRFX_SUCCESS);
594 if (err_code == NRFX_SUCCESS)
595 {
596 /* Creating IPCT connection between the first and the second domain. */
597 err_code = ipct_connection_create(p_src_ipct, p_dst_ipct, p_path);
598 NRFX_ASSERT(err_code == NRFX_SUCCESS);
599 }
600 }
601 if (err_code != NRFX_SUCCESS)
602 {
603 (void)local_connection_remove(p_src_apb, p_src_ipct_apb, *src_dppi_chan);
604 (void)local_connection_remove(p_dst_ipct_apb, p_dst_apb, *dst_dppi_chan);
605 (void)ipct_connection_create(p_src_ipct, p_dst_ipct, p_path);
606 }
607 }
608
609 if (err_code == NRFX_SUCCESS)
610 {
611 p_path->p_src_apb = p_src_apb;
612 p_path->p_dst_apb = p_dst_apb;
613 NRF_DPPI_ENDPOINT_SETUP(eep, *src_dppi_chan);
614 NRF_DPPI_ENDPOINT_SETUP(tep, *dst_dppi_chan);
615 }
616 else
617 {
618 path_cleanup(p_path);
619 }
620 }
621
nrfx_gppi_channel_endpoints_clear(uint8_t channel,uint32_t eep,uint32_t tep)622 void nrfx_gppi_channel_endpoints_clear(uint8_t channel, uint32_t eep, uint32_t tep)
623 {
624 NRFX_ASSERT(tep);
625 NRFX_ASSERT(eep);
626
627 nrfx_err_t err_code;
628 nrfx_interconnect_apb_t const * p_src_apb = (nrfx_interconnect_apb_get(eep));
629 nrfx_interconnect_apb_t const * p_dst_apb = (nrfx_interconnect_apb_get(tep));
630 nrfx_gppi_channels_path_t * p_path = &channels_path[channel];
631 uint8_t * src_dppi_chan = nrfx_interconnect_apb_domain_get(p_src_apb) == NRF_DOMAIN_GLOBAL ?
632 &p_path->dppi_channel :
633 &p_path->local_dppi_channel;
634 uint8_t * dst_dppi_chan = nrfx_interconnect_apb_domain_get(p_dst_apb) == NRF_DOMAIN_GLOBAL ?
635 &p_path->dppi_channel :
636 &p_path->local_dppi_channel;
637
638 NRFX_ASSERT(p_src_apb);
639 NRFX_ASSERT(p_dst_apb);
640 NRFX_ASSERT(p_path->p_src_apb == p_src_apb);
641 NRFX_ASSERT(p_path->p_dst_apb == p_dst_apb);
642
643 if (nrfx_interconnect_apb_domain_get(p_src_apb) == nrfx_interconnect_apb_domain_get(p_dst_apb))
644 {
645 /* Endpoints belongs to the same domain - need to remove one local connection. */
646 NRFX_ASSERT(src_dppi_chan == dst_dppi_chan);
647 err_code = local_connection_remove(p_src_apb, p_dst_apb, *src_dppi_chan);
648 NRFX_ASSERT(err_code == NRFX_SUCCESS);
649 }
650 else
651 {
652 /* Endpoints in different domains - need to remove two local connections and one IPCT connection. */
653 NRFX_ASSERT(src_dppi_chan != dst_dppi_chan);
654 nrfx_interconnect_ipct_t const * p_src_ipct = nrfx_interconnect_ipct_get(p_src_apb);
655 nrfx_interconnect_ipct_t const * p_dst_ipct = nrfx_interconnect_ipct_get(p_dst_apb);
656 nrfx_interconnect_apb_t const * p_src_ipct_apb =
657 nrfx_interconnect_apb_get((uint32_t)p_src_ipct->p_ipct);
658 nrfx_interconnect_apb_t const * p_dst_ipct_apb =
659 nrfx_interconnect_apb_get((uint32_t)p_dst_ipct->p_ipct);
660
661 /* Removing IPCT connection between the first and the second domain. */
662 err_code = ipct_connection_remove(p_src_ipct, p_dst_ipct, p_path);
663 NRFX_ASSERT(err_code == NRFX_SUCCESS);
664 if (err_code == NRFX_SUCCESS)
665 {
666 /* Removing local connection from IPCT peripheral to destination inside the first domain. */
667 err_code = local_connection_remove(p_src_apb, p_src_ipct_apb, *src_dppi_chan);
668 NRFX_ASSERT(err_code == NRFX_SUCCESS);
669 if (err_code == NRFX_SUCCESS)
670 {
671 /* Removing local connection from IPCT peripheral to destination inside the second domain. */
672 err_code = local_connection_remove(p_dst_ipct_apb, p_dst_apb, *dst_dppi_chan);
673 NRFX_ASSERT(err_code == NRFX_SUCCESS);
674 }
675 }
676 }
677 if (err_code == NRFX_SUCCESS)
678 {
679 path_cleanup(p_path);
680 NRF_DPPI_ENDPOINT_CLEAR(eep);
681 NRF_DPPI_ENDPOINT_CLEAR(tep);
682 }
683 NRFX_ASSERT(err_code == NRFX_SUCCESS);
684 }
685
nrfx_gppi_channel_free(uint8_t channel)686 nrfx_err_t nrfx_gppi_channel_free(uint8_t channel)
687 {
688 nrfx_err_t err;
689 nrfx_gppi_channels_path_t * p_path;
690
691 err = nrfx_flag32_free(&m_virtual_channels, channel);
692 if (err != NRFX_SUCCESS)
693 {
694 return err;
695 }
696 p_path = &channels_path[channel];
697 path_cleanup(p_path);
698 return err;
699 }
700
nrfx_gppi_channel_check(uint8_t channel)701 bool nrfx_gppi_channel_check(uint8_t channel)
702 {
703 nrfx_interconnect_apb_t const * p_src_apb = channels_path[channel].p_src_apb;
704 nrfx_interconnect_apb_t const * p_dst_apb = channels_path[channel].p_dst_apb;
705 uint8_t dppi_channel = channels_path[channel].dppi_channel;
706
707 NRFX_ASSERT(nrfx_flag32_is_allocated(m_virtual_channels, channel));
708 if (dppi_channel != CHANNEL_INVALID)
709 {
710 NRFX_ASSERT(p_src_apb);
711 NRFX_ASSERT(p_dst_apb);
712 if (!nrf_dppi_channel_check(p_src_apb->p_dppi, dppi_channel) ||
713 !nrf_dppi_channel_check(p_dst_apb->p_dppi, dppi_channel))
714 {
715 /* At least one of DPPIC channels is not valid. */
716 return false;
717 }
718 if (is_main_connection_needed(p_src_apb, p_dst_apb) &&
719 !nrf_dppi_channel_check(nrfx_interconnect_apb_main_get()->p_dppi, dppi_channel))
720 {
721 /* DPPIC channel for main APB is not valid. */
722 return false;
723 }
724 return true;
725 }
726 return false;
727 }
728
nrfx_gppi_channels_disable_all(void)729 void nrfx_gppi_channels_disable_all(void)
730 {
731 uint32_t mask = ~(uint32_t)m_virtual_channels;
732 while (mask)
733 {
734 uint8_t chan = (uint8_t)NRF_CTZ(mask);
735 dppic_channel_set(chan, false);
736 mask &= ~NRFX_BIT(chan);
737 }
738 }
739
nrfx_gppi_channels_enable(uint32_t mask)740 void nrfx_gppi_channels_enable(uint32_t mask)
741 {
742 while (mask)
743 {
744 uint8_t chan = (uint8_t)NRF_CTZ(mask);
745 dppic_channel_set(chan, true);
746 mask &= ~NRFX_BIT(chan);
747 }
748 }
749
nrfx_gppi_channels_disable(uint32_t mask)750 void nrfx_gppi_channels_disable(uint32_t mask)
751 {
752 // Remove all connections for all channels determined by mask.
753 while (mask)
754 {
755 // Remove assigned channels for all involved DPPICn peripherals.
756 uint8_t chan = (uint8_t)NRF_CTZ(mask);
757 dppic_channel_set(chan, false);
758 mask &= ~NRFX_BIT(chan);
759 }
760 }
761
762 #endif // NRFX_DPPI_ENABLED && (DPPIC_COUNT > 1)
763