1 /*
2 * Copyright (c) 2017, 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 Nordic Semiconductor ASA 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
35 /**
36 * @file
37 * This file implements critical sections used with requests by 802.15.4 driver.
38 *
39 */
40
41 #define NRF_802154_MODULE_ID NRF_802154_DRV_MODULE_ID_CRITICAL_SECTION
42
43 #include "nrf_802154_critical_section.h"
44
45 #include "nrf_802154_assert.h"
46 #include <stdint.h>
47
48 #include "nrf_802154_config.h"
49 #include "nrf_802154_debug.h"
50 #include "nrf_802154_utils.h"
51 #include "rsch/nrf_802154_rsch.h"
52 #include "platform/nrf_802154_platform_sl_lptimer.h"
53 #include "platform/nrf_802154_irq.h"
54
55 #include <nrfx.h>
56
57 #define CMSIS_IRQ_NUM_VECTACTIVE_DIFF 16
58
59 #define NESTED_CRITICAL_SECTION_ALLOWED_PRIORITY_NONE (-1)
60
61 static volatile uint8_t m_nested_critical_section_counter; ///< Counter of nested critical sections
62 static volatile int8_t m_nested_critical_section_allowed_priority; ///< Indicator if nested critical sections are currently allowed
63
64 /***************************************************************************************************
65 * @section Critical sections management
66 **************************************************************************************************/
67
68 /** @brief Enter critical section for RADIO peripheral
69 *
70 * @note RADIO peripheral registers (and NVIC) are modified only when timeslot is granted for the
71 * 802.15.4 driver.
72 */
radio_critical_section_enter(void)73 static void radio_critical_section_enter(void)
74 {
75 if (nrf_802154_rsch_prec_is_approved(RSCH_PREC_RAAL, RSCH_PRIO_MIN_APPROVED))
76 {
77 nrf_802154_irq_disable(nrfx_get_irq_number(NRF_RADIO));
78 }
79 }
80
81 /** @brief Exit critical section for RADIO peripheral
82 *
83 * @note RADIO peripheral registers (and NVIC) are modified only when timeslot is granted for the
84 * 802.15.4 driver.
85 */
radio_critical_section_exit(void)86 static void radio_critical_section_exit(void)
87 {
88 if (nrf_802154_rsch_prec_is_approved(RSCH_PREC_RAAL, RSCH_PRIO_MIN_APPROVED))
89 {
90 nrf_802154_irq_enable(nrfx_get_irq_number(NRF_RADIO));
91 }
92 }
93
94 /** @brief Convert active priority value to int8_t type.
95 *
96 * @param[in] active_priority Active priority in uint32_t format
97 *
98 * @return Active_priority value in int8_t format.
99 */
active_priority_convert(uint32_t active_priority)100 static int8_t active_priority_convert(uint32_t active_priority)
101 {
102 return active_priority == UINT32_MAX ? INT8_MAX : (int8_t)active_priority;
103 }
104
105 /**@brief Returns priority of the currently executing context */
active_vector_priority_get(void)106 static int8_t active_vector_priority_get(void)
107 {
108 return active_priority_convert(nrf_802154_critical_section_active_vector_priority_get());
109 }
110
critical_section_enter(bool forced)111 static bool critical_section_enter(bool forced)
112 {
113 bool result = false;
114 int8_t active_vector_priority;
115 nrf_802154_mcu_critical_state_t mcu_cs;
116
117 /* We assume that preempting interrupts won't change the priority
118 * of the currently executing one.
119 */
120 active_vector_priority = active_vector_priority_get();
121
122 nrf_802154_mcu_critical_enter(mcu_cs);
123
124 if (forced ||
125 (m_nested_critical_section_counter == 0) ||
126 (m_nested_critical_section_allowed_priority == active_vector_priority))
127 {
128 uint8_t cnt = m_nested_critical_section_counter;
129
130 ++cnt;
131 m_nested_critical_section_counter = cnt;
132
133 if (cnt == 1U)
134 {
135 nrf_802154_platform_sl_lptimer_critical_section_enter();
136 radio_critical_section_enter();
137 }
138
139 result = true;
140 }
141
142 nrf_802154_mcu_critical_exit(mcu_cs);
143
144 return result;
145 }
146
critical_section_exit(void)147 static void critical_section_exit(void)
148 {
149 bool succeed = false;
150
151 do
152 {
153 uint8_t cnt;
154 nrf_802154_mcu_critical_state_t mcu_cs;
155
156 nrf_802154_mcu_critical_enter(mcu_cs);
157
158 cnt = m_nested_critical_section_counter;
159
160 NRF_802154_ASSERT(cnt > 0);
161
162 --cnt;
163
164 if (cnt == 0U)
165 {
166 if (nrf_802154_critical_section_rsch_event_is_pending())
167 {
168 nrf_802154_mcu_critical_exit(mcu_cs);
169
170 nrf_802154_critical_section_rsch_process_pending();
171 continue;
172 }
173
174 radio_critical_section_exit();
175 nrf_802154_platform_sl_lptimer_critical_section_exit();
176 }
177
178 m_nested_critical_section_counter = cnt;
179
180 nrf_802154_mcu_critical_exit(mcu_cs);
181
182 succeed = true;
183 }
184 while (!succeed);
185
186 }
187
188 /***************************************************************************************************
189 * @section API functions
190 **************************************************************************************************/
191
nrf_802154_critical_section_init(void)192 void nrf_802154_critical_section_init(void)
193 {
194 m_nested_critical_section_counter = 0;
195 m_nested_critical_section_allowed_priority = NESTED_CRITICAL_SECTION_ALLOWED_PRIORITY_NONE;
196 }
197
nrf_802154_critical_section_enter(void)198 bool nrf_802154_critical_section_enter(void)
199 {
200 bool result;
201
202 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
203
204 result = critical_section_enter(false);
205
206 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
207
208 return result;
209 }
210
nrf_802154_critical_section_forcefully_enter(void)211 void nrf_802154_critical_section_forcefully_enter(void)
212 {
213 bool critical_section_entered;
214
215 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
216
217 critical_section_entered = critical_section_enter(true);
218 NRF_802154_ASSERT(critical_section_entered);
219 (void)critical_section_entered;
220
221 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
222 }
223
nrf_802154_critical_section_exit(void)224 void nrf_802154_critical_section_exit(void)
225 {
226 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
227
228 critical_section_exit();
229
230 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
231 }
232
nrf_802154_critical_section_nesting_allow(void)233 void nrf_802154_critical_section_nesting_allow(void)
234 {
235 NRF_802154_ASSERT(m_nested_critical_section_allowed_priority ==
236 NESTED_CRITICAL_SECTION_ALLOWED_PRIORITY_NONE);
237 NRF_802154_ASSERT(m_nested_critical_section_counter >= 1);
238
239 m_nested_critical_section_allowed_priority = active_vector_priority_get();
240 }
241
nrf_802154_critical_section_nesting_deny(void)242 void nrf_802154_critical_section_nesting_deny(void)
243 {
244 NRF_802154_ASSERT(m_nested_critical_section_allowed_priority >= 0);
245 NRF_802154_ASSERT(m_nested_critical_section_counter >= 1);
246
247 m_nested_critical_section_allowed_priority = NESTED_CRITICAL_SECTION_ALLOWED_PRIORITY_NONE;
248 }
249
nrf_802154_critical_section_is_nested(void)250 bool nrf_802154_critical_section_is_nested(void)
251 {
252 return m_nested_critical_section_counter > 1;
253 }
254
nrf_802154_critical_section_active_vector_priority_get(void)255 uint32_t nrf_802154_critical_section_active_vector_priority_get(void)
256 {
257 uint32_t active_priority;
258
259 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
260 /* The nRF52_bsim does not implement the SCB. Use its APIs to get the
261 * currently running ISR instead */
262 int irq_number = posix_get_current_irq();
263
264 if (irq_number == -1) /* not in interrupt */
265 {
266 return UINT32_MAX;
267 }
268 #else
269 uint32_t active_vector_id = (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) >> SCB_ICSR_VECTACTIVE_Pos;
270 IRQn_Type irq_number;
271
272 // Check if this function is called from main thread.
273 if (active_vector_id == 0)
274 {
275 return UINT32_MAX;
276 }
277
278 irq_number = (IRQn_Type)((int32_t)active_vector_id - CMSIS_IRQ_NUM_VECTACTIVE_DIFF);
279
280 NRF_802154_ASSERT(irq_number >= SVCall_IRQn);
281 #endif
282 active_priority = NVIC_GetPriority(irq_number);
283
284 return active_priority;
285 }
286