1 // Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <stdint.h>
15 #include "esp_attr.h"
16 #include "esp_err.h"
17 #include "esp_intr_alloc.h"
18 #include "soc/periph_defs.h"
19 #include "soc/system_reg.h"
20 #include "hal/cpu_hal.h"
21 #include "freertos/FreeRTOS.h"
22 #include "freertos/portmacro.h"
23
24 #define REASON_YIELD BIT(0)
25 #define REASON_FREQ_SWITCH BIT(1)
26 #define REASON_PRINT_BACKTRACE BIT(2)
27
28 static portMUX_TYPE reason_spinlock = portMUX_INITIALIZER_UNLOCKED;
29 static volatile uint32_t reason[portNUM_PROCESSORS];
30
31 // TODO ESP32-C3 IDF-2449
esp_crosscore_isr_handle_yield(void)32 static inline void IRAM_ATTR esp_crosscore_isr_handle_yield(void)
33 {
34 portYIELD_FROM_ISR();
35 }
36
esp_crosscore_isr(void * arg)37 static void IRAM_ATTR esp_crosscore_isr(void *arg)
38 {
39 uint32_t my_reason_val;
40 //A pointer to the correct reason array item is passed to this ISR.
41 volatile uint32_t *my_reason = arg;
42
43 //Clear the interrupt first.
44 WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, 0);
45 //Grab the reason and clear it.
46 portENTER_CRITICAL_ISR(&reason_spinlock);
47 my_reason_val = *my_reason;
48 *my_reason = 0;
49 portEXIT_CRITICAL_ISR(&reason_spinlock);
50
51 //Check what we need to do.
52 if (my_reason_val & REASON_YIELD) {
53 esp_crosscore_isr_handle_yield();
54 }
55 if (my_reason_val & REASON_FREQ_SWITCH) {
56 /* Nothing to do here; the frequency switch event was already
57 * handled by a hook in xtensa_vectors.S. Could be used in the future
58 * to allow DFS features without the extra latency of the ISR hook.
59 */
60 }
61 }
62
63 // Initialize the crosscore interrupt on this core.
esp_crosscore_int_init(void)64 void esp_crosscore_int_init(void)
65 {
66 portENTER_CRITICAL(&reason_spinlock);
67 reason[cpu_hal_get_core_id()] = 0;
68 portEXIT_CRITICAL(&reason_spinlock);
69 esp_err_t err = esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void *)&reason[0], NULL);
70 assert(err == ESP_OK);
71 }
72
esp_crosscore_int_send(int core_id,uint32_t reason_mask)73 static void IRAM_ATTR esp_crosscore_int_send(int core_id, uint32_t reason_mask)
74 {
75 assert(core_id < portNUM_PROCESSORS);
76 //Mark the reason we interrupt the other CPU
77 portENTER_CRITICAL(&reason_spinlock);
78 reason[core_id] |= reason_mask;
79 portEXIT_CRITICAL(&reason_spinlock);
80 //Poke the other CPU.
81 WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, SYSTEM_CPU_INTR_FROM_CPU_0);
82 }
83
esp_crosscore_int_send_yield(int core_id)84 void IRAM_ATTR esp_crosscore_int_send_yield(int core_id)
85 {
86 esp_crosscore_int_send(core_id, REASON_YIELD);
87 }
88
esp_crosscore_int_send_freq_switch(int core_id)89 void IRAM_ATTR esp_crosscore_int_send_freq_switch(int core_id)
90 {
91 esp_crosscore_int_send(core_id, REASON_FREQ_SWITCH);
92 }
93
esp_crosscore_int_send_print_backtrace(int core_id)94 void IRAM_ATTR esp_crosscore_int_send_print_backtrace(int core_id)
95 {
96 esp_crosscore_int_send(core_id, REASON_PRINT_BACKTRACE);
97 }
98