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