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