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