1 /*
2 * SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stddef.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <assert.h>
11 #include "esp_err.h"
12 #include "esp_attr.h"
13 #include "soc/soc.h"
14 #include "soc/dport_reg.h"
15 #ifndef CONFIG_IDF_TARGET_ESP32
16 #include "soc/periph_defs.h"
17 #include "soc/system_reg.h"
18 #endif
19 #include "freertos/FreeRTOS.h"
20 #include "freertos/task.h"
21 #include "freertos/portmacro.h"
22 #include "esp_intr_alloc.h"
23 #include "esp_private/esp_ipc_isr.h"
24 #include "esp_ipc_isr.h"
25 #include "xtensa/core-macros.h"
26 #include "sdkconfig.h"
27
28 static portMUX_TYPE s_ipc_isr_mux = portMUX_INITIALIZER_UNLOCKED;
29 uint32_t volatile esp_ipc_isr_start_fl; // the flag shows that it is about to run esp_ipc_func()
30 uint32_t volatile esp_ipc_isr_end_fl = 1; // the flag shows that esp_ipc_func() is done
31 esp_ipc_isr_func_t volatile esp_ipc_func; // the function which will be run in the ipc_isr context
32 void * volatile esp_ipc_func_arg; // the argument of esp_ipc_func()
33
34 typedef enum {
35 STALL_STATE_IDLE = 0,
36 STALL_STATE_RUNNING = 1,
37 } stall_state_t;
38
39 static stall_state_t volatile s_stall_state = STALL_STATE_IDLE;
40 static int32_t volatile s_count_of_nested_calls[portNUM_PROCESSORS] = { 0 };
41 static BaseType_t s_stored_interrupt_level;
42 static uint32_t volatile esp_ipc_isr_finish_cmd;
43
44
45 /**
46 * @brief Type of calling
47 */
48 typedef enum {
49 IPC_ISR_WAIT_FOR_START = 0, /*!< The caller is waiting for the start */
50 IPC_ISR_WAIT_FOR_END = 1, /*!< The caller is waiting for the end */
51 } esp_ipc_isr_wait_t;
52
53 #define IPC_ISR_ENTER_CRITICAL() portENTER_CRITICAL_SAFE(&s_ipc_isr_mux)
54 #define IPC_ISR_EXIT_CRITICAL() portEXIT_CRITICAL_SAFE(&s_ipc_isr_mux)
55
56 static void esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* arg, esp_ipc_isr_wait_t wait_for);
57
58
59 /* Initializing IPC_ISR */
60
esp_ipc_isr_init(void)61 void esp_ipc_isr_init(void)
62 {
63 const uint32_t cpuid = xPortGetCoreID();
64 uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE + cpuid; // ETS_FROM_CPU_INTR2_SOURCE and ETS_FROM_CPU_INTR3_SOURCE
65 ESP_INTR_DISABLE(ETS_IPC_ISR_INUM);
66 esp_rom_route_intr_matrix(cpuid, intr_source, ETS_IPC_ISR_INUM);
67 ESP_INTR_ENABLE(ETS_IPC_ISR_INUM);
68
69 if (cpuid != 0) {
70 s_stall_state = STALL_STATE_RUNNING;
71 }
72 }
73
74 /* End initializing IPC_ISR */
75
76
77 /* Public API functions */
78
esp_ipc_isr_asm_call(esp_ipc_isr_func_t func,void * arg)79 void IRAM_ATTR esp_ipc_isr_asm_call(esp_ipc_isr_func_t func, void* arg)
80 {
81 IPC_ISR_ENTER_CRITICAL();
82 esp_ipc_isr_call_and_wait(func, arg, IPC_ISR_WAIT_FOR_START);
83 IPC_ISR_EXIT_CRITICAL();
84 }
85
esp_ipc_isr_asm_call_blocking(esp_ipc_isr_func_t func,void * arg)86 void IRAM_ATTR esp_ipc_isr_asm_call_blocking(esp_ipc_isr_func_t func, void* arg)
87 {
88 IPC_ISR_ENTER_CRITICAL();
89 esp_ipc_isr_call_and_wait(func, arg, IPC_ISR_WAIT_FOR_END);
90 IPC_ISR_EXIT_CRITICAL();
91 }
92
93 // This asm function is from esp_ipc_isr_routines.S.
94 // It is waiting for the finish_cmd command in a loop.
95 void esp_ipc_isr_waiting_for_finish_cmd(void* finish_cmd);
96
97 /*
98 * esp_ipc_isr_stall_other_cpu is used for:
99 * - stall other CPU,
100 * - do protection when dual core access DPORT internal register and APB register via DPORT simultaneously.
101 * This function will be initialize after FreeRTOS startup.
102 * When cpu0 wants to access DPORT register, it should notify cpu1 enter in high-priority interrupt for be mute.
103 * When cpu1 already in high-priority interrupt, cpu0 can access DPORT register.
104 * Currently, cpu1 will wait for cpu0 finish access and exit high-priority interrupt.
105 */
esp_ipc_isr_stall_other_cpu(void)106 void IRAM_ATTR esp_ipc_isr_stall_other_cpu(void)
107 {
108 #if CONFIG_FREERTOS_SMP
109 /*
110 Temporary workaround to prevent deadlocking on the SMP FreeRTOS kernel lock after stalling the other CPU.
111 See IDF-5257
112 */
113 taskENTER_CRITICAL();
114 #endif
115 if (s_stall_state == STALL_STATE_RUNNING) {
116 #if CONFIG_FREERTOS_SMP
117 BaseType_t intLvl = portDISABLE_INTERRUPTS();
118 #else
119 BaseType_t intLvl = portSET_INTERRUPT_MASK_FROM_ISR();
120 #endif
121 const uint32_t cpu_id = xPortGetCoreID();
122 if (s_count_of_nested_calls[cpu_id]++ == 0) {
123 IPC_ISR_ENTER_CRITICAL();
124 s_stored_interrupt_level = intLvl;
125 esp_ipc_isr_finish_cmd = 0;
126 esp_ipc_isr_call_and_wait(&esp_ipc_isr_waiting_for_finish_cmd, (void*)&esp_ipc_isr_finish_cmd, IPC_ISR_WAIT_FOR_START);
127 return;
128 }
129
130 /* Interrupts are already disabled by the parent, we're nested here. */
131 #if CONFIG_FREERTOS_SMP
132 portRESTORE_INTERRUPTS(intLvl);
133 #else
134 portCLEAR_INTERRUPT_MASK_FROM_ISR(intLvl);
135 #endif
136 }
137 }
138
esp_ipc_isr_release_other_cpu(void)139 void IRAM_ATTR esp_ipc_isr_release_other_cpu(void)
140 {
141 if (s_stall_state == STALL_STATE_RUNNING) {
142 const uint32_t cpu_id = xPortGetCoreID();
143 if (--s_count_of_nested_calls[cpu_id] == 0) {
144 esp_ipc_isr_finish_cmd = 1;
145 // Make sure end flag is cleared and esp_ipc_isr_waiting_for_finish_cmd is done.
146 while (!esp_ipc_isr_end_fl) {};
147 IPC_ISR_EXIT_CRITICAL();
148 #if CONFIG_FREERTOS_SMP
149 portRESTORE_INTERRUPTS(s_stored_interrupt_level);
150 #else
151 portCLEAR_INTERRUPT_MASK_FROM_ISR(s_stored_interrupt_level);
152 #endif
153 } else if (s_count_of_nested_calls[cpu_id] < 0) {
154 assert(0);
155 }
156 }
157 #if CONFIG_FREERTOS_SMP
158 /*
159 Temporary workaround to prevent deadlocking on the SMP FreeRTOS kernel lock after stalling the other CPU.
160 See IDF-5257
161 */
162 taskEXIT_CRITICAL();
163 #endif
164 }
165
esp_ipc_isr_stall_pause(void)166 void IRAM_ATTR esp_ipc_isr_stall_pause(void)
167 {
168 IPC_ISR_ENTER_CRITICAL();
169 s_stall_state = STALL_STATE_IDLE;
170 IPC_ISR_EXIT_CRITICAL();
171 }
172
esp_ipc_isr_stall_abort(void)173 void IRAM_ATTR esp_ipc_isr_stall_abort(void)
174 {
175 //Note: We don't enter a critical section here as we are calling this from a panic.
176 s_stall_state = STALL_STATE_IDLE;
177 }
178
esp_ipc_isr_stall_resume(void)179 void IRAM_ATTR esp_ipc_isr_stall_resume(void)
180 {
181 IPC_ISR_ENTER_CRITICAL();
182 s_stall_state = STALL_STATE_RUNNING;
183 IPC_ISR_EXIT_CRITICAL();
184 }
185
186 /* End public API functions */
187
188
189 /* Private functions*/
190
esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func,void * arg,esp_ipc_isr_wait_t wait_for)191 static void IRAM_ATTR esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* arg, esp_ipc_isr_wait_t wait_for)
192 {
193 const uint32_t cpu_id = xPortGetCoreID();
194
195 // waiting for the end of the previous call
196 while (!esp_ipc_isr_end_fl) {};
197
198 esp_ipc_func = func;
199 esp_ipc_func_arg = arg;
200
201 esp_ipc_isr_start_fl = 0;
202 esp_ipc_isr_end_fl = 0;
203
204 if (cpu_id == 0) {
205 // it runs an interrupt on cpu1
206 DPORT_REG_WRITE(SYSTEM_CPU_INTR_FROM_CPU_3_REG, SYSTEM_CPU_INTR_FROM_CPU_3);
207 } else {
208 // it runs an interrupt on cpu0
209 DPORT_REG_WRITE(SYSTEM_CPU_INTR_FROM_CPU_2_REG, SYSTEM_CPU_INTR_FROM_CPU_2);
210 }
211
212 // IPC_ISR handler will be called and `...isr_start` and `...isr_end` will be updated there
213
214 if (wait_for == IPC_ISR_WAIT_FOR_START) {
215 while (!esp_ipc_isr_start_fl) {};
216 } else {
217 // IPC_ISR_WAIT_FOR_END
218 while (!esp_ipc_isr_end_fl) {};
219 }
220 }
221
222 /* End private functions*/
223