1 /*
2  * Copyright (c) 2015 - 2025, 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 <nrfx.h>
35 
36 #if NRFX_CHECK(NRFX_PPI_ENABLED)
37 
38 #include <nrfx_ppi.h>
39 #include <helpers/nrfx_flag32_allocator.h>
40 
41 #define NRFX_LOG_MODULE PPI
42 #include <nrfx_log.h>
43 
44 /** @brief Bitmask representing channels availability. */
45 static nrfx_atomic_t m_channels_allocated = NRFX_PPI_PROG_APP_CHANNELS_MASK;
46 
47 /** @brief Bitmask representing groups availability. */
48 static nrfx_atomic_t m_groups_allocated = NRFX_PPI_ALL_APP_GROUPS_MASK;
49 
50 
51 /**
52  * @brief Compute a group mask (needed for driver internals, not used for NRF_PPI registers).
53  *
54  * @param[in] group Group number to transform to a mask.
55  *
56  * @retval Group mask.
57  */
group_to_mask(nrf_ppi_channel_group_t group)58 static uint32_t group_to_mask(nrf_ppi_channel_group_t group)
59 {
60     return (1uL << (uint32_t) group);
61 }
62 
63 
64 /**
65  * @brief Check whether a channel is a programmable channel and can be used by an application.
66  *
67  * @param[in] channel Channel to check.
68  *
69  * @retval true  The channel is a programmable application channel.
70  * @retval false The channel is used by a stack (for example SoftDevice) or is preprogrammed.
71  */
is_programmable_app_channel(nrf_ppi_channel_t channel)72 static bool is_programmable_app_channel(nrf_ppi_channel_t channel)
73 {
74     return ((NRFX_PPI_PROG_APP_CHANNELS_MASK & nrfx_ppi_channel_to_mask(channel)) != 0);
75 }
76 
77 
78 /**
79  * @brief Check whether channels can be used by an application.
80  *
81  * @param[in] channel_mask Channel mask to check.
82  *
83  * @retval true  All specified channels can be used by an application.
84  * @retval false At least one specified channel is used by a stack (for example SoftDevice).
85  */
are_app_channels(uint32_t channel_mask)86 static bool are_app_channels(uint32_t channel_mask)
87 {
88     return ((~(NRFX_PPI_ALL_APP_CHANNELS_MASK) & channel_mask) == 0);
89 }
90 
91 
92 /**
93  * @brief Check whether a channel can be used by an application.
94  *
95  * @param[in] channel Channel to check.
96  *
97  * @retval true  The channel can be used by an application.
98  * @retval false The channel is used by a stack (for example SoftDevice).
99  */
is_app_channel(nrf_ppi_channel_t channel)100 static bool is_app_channel(nrf_ppi_channel_t channel)
101 {
102     return are_app_channels(nrfx_ppi_channel_to_mask(channel));
103 }
104 
105 
106 /**
107  * @brief Check whether a channel group can be used by an application.
108  *
109  * @param[in] group Group to check.
110  *
111  * @retval true  The group is an application group.
112  * @retval false The group is not an application group (this group either does not exist or
113  *               it is used by a stack (for example SoftDevice)).
114  */
is_app_group(nrf_ppi_channel_group_t group)115 static bool is_app_group(nrf_ppi_channel_group_t group)
116 {
117     return ((NRFX_PPI_ALL_APP_GROUPS_MASK & group_to_mask(group)) != 0);
118 }
119 
120 
nrfx_ppi_free_all(void)121 void nrfx_ppi_free_all(void)
122 {
123     uint32_t mask = NRFX_PPI_ALL_APP_GROUPS_MASK;
124 
125     // Disable all channels and groups
126     nrf_ppi_channels_disable(NRF_PPI, NRFX_PPI_ALL_APP_CHANNELS_MASK);
127 
128     for (uint8_t group_idx = NRF_PPI_CHANNEL_GROUP0; mask != 0; group_idx++)
129     {
130         nrf_ppi_channel_group_t group = (nrf_ppi_channel_group_t)group_idx;
131         if (mask & group_to_mask(group))
132         {
133             nrf_ppi_group_clear(NRF_PPI, group);
134         }
135         mask &= ~group_to_mask(group);
136     }
137     nrfx_flag32_init(&m_channels_allocated, NRFX_PPI_PROG_APP_CHANNELS_MASK);
138     nrfx_flag32_init(&m_groups_allocated, NRFX_PPI_ALL_APP_GROUPS_MASK);
139 }
140 
141 
nrfx_ppi_channel_alloc(nrf_ppi_channel_t * p_channel)142 nrfx_err_t nrfx_ppi_channel_alloc(nrf_ppi_channel_t * p_channel)
143 {
144     return nrfx_flag32_alloc(&m_channels_allocated, (uint8_t *)p_channel);
145 }
146 
147 
nrfx_ppi_channel_free(nrf_ppi_channel_t channel)148 nrfx_err_t nrfx_ppi_channel_free(nrf_ppi_channel_t channel)
149 {
150     if (!is_programmable_app_channel(channel))
151     {
152         return NRFX_ERROR_INVALID_PARAM;
153     }
154 
155     nrf_ppi_channel_disable(NRF_PPI, channel);
156 
157     return nrfx_flag32_free(&m_channels_allocated, channel);
158 }
159 
160 
nrfx_ppi_channel_assign(nrf_ppi_channel_t channel,uint32_t eep,uint32_t tep)161 nrfx_err_t nrfx_ppi_channel_assign(nrf_ppi_channel_t channel, uint32_t eep, uint32_t tep)
162 {
163     if ((uint32_t *)eep == NULL || (uint32_t *)tep == NULL)
164     {
165         return NRFX_ERROR_NULL;
166     }
167 
168     nrfx_err_t err_code = NRFX_SUCCESS;
169 
170     if (!is_programmable_app_channel(channel))
171     {
172         err_code = NRFX_ERROR_INVALID_PARAM;
173     }
174     else if (!nrfx_flag32_is_allocated(m_channels_allocated, channel))
175     {
176         err_code = NRFX_ERROR_INVALID_STATE;
177     }
178     else
179     {
180         nrf_ppi_channel_endpoint_setup(NRF_PPI, channel, eep, tep);
181         NRFX_LOG_INFO("Assigned channel: %d, event end point: %x, task end point: %x.",
182                       channel,
183                       eep,
184                       tep);
185     }
186     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
187     return err_code;
188 }
189 
nrfx_ppi_channel_fork_assign(nrf_ppi_channel_t channel,uint32_t fork_tep)190 nrfx_err_t nrfx_ppi_channel_fork_assign(nrf_ppi_channel_t channel, uint32_t fork_tep)
191 {
192     nrfx_err_t err_code = NRFX_SUCCESS;
193 #ifdef PPI_FEATURE_FORKS_PRESENT
194     if (!nrfx_flag32_is_allocated(m_channels_allocated, channel))
195     {
196         err_code = NRFX_ERROR_INVALID_STATE;
197     }
198     else
199     {
200         nrf_ppi_fork_endpoint_setup(NRF_PPI, channel, fork_tep);
201         NRFX_LOG_INFO("Fork assigned channel: %d, task end point: %d.", channel, fork_tep);
202     }
203     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
204     return err_code;
205 #else
206     (void)channel;
207     (void)fork_tep;
208 
209     err_code = NRFX_ERROR_NOT_SUPPORTED;
210     NRFX_LOG_WARNING("Function: %s, error code: %s.",
211                      __func__,
212                      NRFX_LOG_ERROR_STRING_GET(err_code));
213     return err_code;
214 #endif
215 }
216 
nrfx_ppi_channel_enable(nrf_ppi_channel_t channel)217 nrfx_err_t nrfx_ppi_channel_enable(nrf_ppi_channel_t channel)
218 {
219     nrfx_err_t err_code = NRFX_SUCCESS;
220 
221     if (!is_app_channel(channel))
222     {
223         err_code = NRFX_ERROR_INVALID_PARAM;
224     }
225     else if (is_programmable_app_channel(channel) &&
226              !nrfx_flag32_is_allocated(m_channels_allocated, channel))
227     {
228         err_code = NRFX_ERROR_INVALID_STATE;
229     }
230     else
231     {
232         nrf_ppi_channel_enable(NRF_PPI, channel);
233     }
234     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
235     return err_code;
236 }
237 
238 
nrfx_ppi_channel_disable(nrf_ppi_channel_t channel)239 nrfx_err_t nrfx_ppi_channel_disable(nrf_ppi_channel_t channel)
240 {
241     nrfx_err_t err_code = NRFX_SUCCESS;
242 
243     if (!is_app_channel(channel))
244     {
245         err_code = NRFX_ERROR_INVALID_PARAM;
246     }
247     else if (is_programmable_app_channel(channel) &&
248              !nrfx_flag32_is_allocated(m_channels_allocated, channel))
249     {
250         err_code = NRFX_ERROR_INVALID_STATE;
251     }
252     else
253     {
254         nrf_ppi_channel_disable(NRF_PPI, channel);
255         err_code = NRFX_SUCCESS;
256     }
257     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
258     return err_code;
259 }
260 
261 
nrfx_ppi_group_alloc(nrf_ppi_channel_group_t * p_group)262 nrfx_err_t nrfx_ppi_group_alloc(nrf_ppi_channel_group_t * p_group)
263 {
264     return nrfx_flag32_alloc(&m_groups_allocated, (uint8_t *)p_group);
265 }
266 
267 
nrfx_ppi_group_free(nrf_ppi_channel_group_t group)268 nrfx_err_t nrfx_ppi_group_free(nrf_ppi_channel_group_t group)
269 {
270     nrf_ppi_group_disable(NRF_PPI, group);
271     return nrfx_flag32_free(&m_groups_allocated, group);
272 }
273 
274 
nrfx_ppi_group_enable(nrf_ppi_channel_group_t group)275 nrfx_err_t nrfx_ppi_group_enable(nrf_ppi_channel_group_t group)
276 {
277     nrfx_err_t err_code = NRFX_SUCCESS;
278 
279     if (!is_app_group(group))
280     {
281         err_code = NRFX_ERROR_INVALID_PARAM;
282     }
283     else if (!nrfx_flag32_is_allocated(m_groups_allocated, group))
284     {
285         err_code = NRFX_ERROR_INVALID_STATE;
286     }
287     else
288     {
289         nrf_ppi_group_enable(NRF_PPI, group);
290     }
291     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
292     return err_code;
293 }
294 
295 
nrfx_ppi_group_disable(nrf_ppi_channel_group_t group)296 nrfx_err_t nrfx_ppi_group_disable(nrf_ppi_channel_group_t group)
297 {
298     nrfx_err_t err_code = NRFX_SUCCESS;
299 
300     if (!is_app_group(group))
301     {
302         err_code = NRFX_ERROR_INVALID_PARAM;
303     }
304     else
305     {
306         nrf_ppi_group_disable(NRF_PPI, group);
307     }
308     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
309     return err_code;
310 }
311 
nrfx_ppi_channels_remove_from_group(uint32_t channel_mask,nrf_ppi_channel_group_t group)312 nrfx_err_t nrfx_ppi_channels_remove_from_group(uint32_t                channel_mask,
313                                                nrf_ppi_channel_group_t group)
314 {
315     nrfx_err_t err_code = NRFX_SUCCESS;
316 
317     if (!is_app_group(group))
318     {
319         err_code = NRFX_ERROR_INVALID_PARAM;
320     }
321     else if (!nrfx_flag32_is_allocated(m_groups_allocated, group))
322     {
323         err_code = NRFX_ERROR_INVALID_STATE;
324     }
325     else if (!are_app_channels(channel_mask))
326     {
327         err_code = NRFX_ERROR_INVALID_PARAM;
328     }
329     else
330     {
331         NRFX_CRITICAL_SECTION_ENTER();
332         nrf_ppi_channels_remove_from_group(NRF_PPI, channel_mask, group);
333         NRFX_CRITICAL_SECTION_EXIT();
334     }
335     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
336     return err_code;
337 }
338 
nrfx_ppi_channels_include_in_group(uint32_t channel_mask,nrf_ppi_channel_group_t group)339 nrfx_err_t nrfx_ppi_channels_include_in_group(uint32_t                channel_mask,
340                                               nrf_ppi_channel_group_t group)
341 {
342     nrfx_err_t err_code = NRFX_SUCCESS;
343 
344     if (!is_app_group(group))
345     {
346         err_code = NRFX_ERROR_INVALID_PARAM;
347     }
348     else if (!nrfx_flag32_is_allocated(m_groups_allocated, group))
349     {
350         err_code = NRFX_ERROR_INVALID_STATE;
351     }
352     else if (!are_app_channels(channel_mask))
353     {
354         err_code = NRFX_ERROR_INVALID_PARAM;
355     }
356     else
357     {
358         NRFX_CRITICAL_SECTION_ENTER();
359         nrf_ppi_channels_include_in_group(NRF_PPI, channel_mask, group);
360         NRFX_CRITICAL_SECTION_EXIT();
361     }
362     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
363     return err_code;
364 }
365 #endif // NRFX_CHECK(NRFX_PPI_ENABLED)
366