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