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