/* * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(esp32_intc, CONFIG_LOG_DEFAULT_LEVEL); #define ETS_INTERNAL_TIMER0_INTR_NO 6 #define ETS_INTERNAL_TIMER1_INTR_NO 15 #define ETS_INTERNAL_TIMER2_INTR_NO 16 #define ETS_INTERNAL_SW0_INTR_NO 7 #define ETS_INTERNAL_SW1_INTR_NO 29 #define ETS_INTERNAL_PROFILING_INTR_NO 11 #define VECDESC_FL_RESERVED (1 << 0) #define VECDESC_FL_INIRAM (1 << 1) #define VECDESC_FL_SHARED (1 << 2) #define VECDESC_FL_NONSHARED (1 << 3) /* * Define this to debug the choices made when allocating the interrupt. This leads to much debugging * output within a critical region, which can lead to weird effects like e.g. the interrupt watchdog * being triggered, that is why it is separate from the normal LOG* scheme. */ #ifdef CONFIG_INTC_ESP32_DECISIONS_LOG # define INTC_LOG(...) LOG_INF(__VA_ARGS__) #else # define INTC_LOG(...) do {} while (false) #endif /* Typedef for C-callable interrupt handler function */ typedef void (*intc_handler_t)(void *); typedef void (*intc_dyn_handler_t)(const void *); /* shared critical section context */ static int esp_intc_csec; static inline void esp_intr_lock(void) { esp_intc_csec = irq_lock(); } static inline void esp_intr_unlock(void) { irq_unlock(esp_intc_csec); } /* * Interrupt handler table and unhandled interrupt routine. Duplicated * from xtensa_intr.c... it's supposed to be private, but we need to look * into it in order to see if someone allocated an int using * set_interrupt_handler. */ struct intr_alloc_table_entry { void (*handler)(void *arg); void *arg; }; /* Default handler for unhandled interrupts. */ void IRAM_ATTR default_intr_handler(void *arg) { esp_rom_printf("Unhandled interrupt %d on cpu %d!\n", (int)arg, esp_cpu_get_core_id()); } static struct intr_alloc_table_entry intr_alloc_table[ESP_INTC_INTS_NUM * CONFIG_MP_MAX_NUM_CPUS]; static void set_interrupt_handler(int n, intc_handler_t f, void *arg) { irq_disable(n); intr_alloc_table[n * CONFIG_MP_MAX_NUM_CPUS].handler = f; irq_connect_dynamic(n, 0, (intc_dyn_handler_t)f, arg, 0); } /* Linked list of vector descriptions, sorted by cpu.intno value */ static struct vector_desc_t *vector_desc_head; /* implicitly initialized to NULL */ /* This bitmask has an 1 if the int should be disabled when the flash is disabled. */ static uint32_t non_iram_int_mask[CONFIG_MP_MAX_NUM_CPUS]; /* This bitmask has 1 in it if the int was disabled using esp_intr_noniram_disable. */ static uint32_t non_iram_int_disabled[CONFIG_MP_MAX_NUM_CPUS]; static bool non_iram_int_disabled_flag[CONFIG_MP_MAX_NUM_CPUS]; /* * Inserts an item into vector_desc list so that the list is sorted * with an incrementing cpu.intno value. */ static void insert_vector_desc(struct vector_desc_t *to_insert) { struct vector_desc_t *vd = vector_desc_head; struct vector_desc_t *prev = NULL; while (vd != NULL) { if (vd->cpu > to_insert->cpu) { break; } if (vd->cpu == to_insert->cpu && vd->intno >= to_insert->intno) { break; } prev = vd; vd = vd->next; } if ((vector_desc_head == NULL) || (prev == NULL)) { /* First item */ to_insert->next = vd; vector_desc_head = to_insert; } else { prev->next = to_insert; to_insert->next = vd; } } /* Returns a vector_desc entry for an intno/cpu, or NULL if none exists. */ static struct vector_desc_t *find_desc_for_int(int intno, int cpu) { struct vector_desc_t *vd = vector_desc_head; while (vd != NULL) { if (vd->cpu == cpu && vd->intno == intno) { break; } vd = vd->next; } return vd; } /* * Returns a vector_desc entry for an intno/cpu. * Either returns a preexisting one or allocates a new one and inserts * it into the list. Returns NULL on malloc fail. */ static struct vector_desc_t *get_desc_for_int(int intno, int cpu) { struct vector_desc_t *vd = find_desc_for_int(intno, cpu); if (vd == NULL) { struct vector_desc_t *newvd = k_malloc(sizeof(struct vector_desc_t)); if (newvd == NULL) { return NULL; } memset(newvd, 0, sizeof(struct vector_desc_t)); newvd->intno = intno; newvd->cpu = cpu; insert_vector_desc(newvd); return newvd; } else { return vd; } } /* * Returns a vector_desc entry for an source, the cpu parameter is used * to tell GPIO_INT and GPIO_NMI from different CPUs */ static struct vector_desc_t *find_desc_for_source(int source, int cpu) { struct vector_desc_t *vd = vector_desc_head; while (vd != NULL) { if (!(vd->flags & VECDESC_FL_SHARED)) { if (vd->source == source && cpu == vd->cpu) { break; } } else if (vd->cpu == cpu) { /* check only shared vds for the correct cpu, otherwise skip */ bool found = false; struct shared_vector_desc_t *svd = vd->shared_vec_info; assert(svd != NULL); while (svd) { if (svd->source == source) { found = true; break; } svd = svd->next; } if (found) { break; } } vd = vd->next; } return vd; } void esp_intr_initialize(void) { unsigned int num_cpus = arch_num_cpus(); for (size_t i = 0; i < (ESP_INTC_INTS_NUM * num_cpus); ++i) { intr_alloc_table[i].handler = default_intr_handler; intr_alloc_table[i].arg = (void *)i; } } int esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) { if (intno >= ESP_INTC_INTS_NUM) { return -EINVAL; } if (cpu >= arch_num_cpus()) { return -EINVAL; } esp_intr_lock(); struct vector_desc_t *vd = get_desc_for_int(intno, cpu); if (vd == NULL) { esp_intr_unlock(); return -ENOMEM; } vd->flags = VECDESC_FL_SHARED; if (is_int_ram) { vd->flags |= VECDESC_FL_INIRAM; } esp_intr_unlock(); return 0; } int esp_intr_reserve(int intno, int cpu) { if (intno >= ESP_INTC_INTS_NUM) { return -EINVAL; } if (cpu >= arch_num_cpus()) { return -EINVAL; } esp_intr_lock(); struct vector_desc_t *vd = get_desc_for_int(intno, cpu); if (vd == NULL) { esp_intr_unlock(); return -ENOMEM; } vd->flags = VECDESC_FL_RESERVED; esp_intr_unlock(); return 0; } /* Returns true if handler for interrupt is not the default unhandled interrupt handler */ static bool intr_has_handler(int intr, int cpu) { bool r; r = intr_alloc_table[intr * CONFIG_MP_MAX_NUM_CPUS + cpu].handler != default_intr_handler; return r; } static bool is_vect_desc_usable(struct vector_desc_t *vd, int flags, int cpu, int force) { /* Check if interrupt is not reserved by design */ int x = vd->intno; esp_cpu_intr_desc_t intr_desc; esp_cpu_intr_get_desc(cpu, x, &intr_desc); if (intr_desc.flags & ESP_CPU_INTR_DESC_FLAG_RESVD) { INTC_LOG("....Unusable: reserved"); return false; } if (intr_desc.flags & ESP_CPU_INTR_DESC_FLAG_SPECIAL && force == -1) { INTC_LOG("....Unusable: special-purpose int"); return false; } #ifndef SOC_CPU_HAS_FLEXIBLE_INTC /* Check if the interrupt priority is acceptable */ if (!(flags & (1 << intr_desc.priority))) { INTC_LOG("....Unusable: incompatible priority"); return false; } /* check if edge/level type matches what we want */ if (((flags & ESP_INTR_FLAG_EDGE) && (intr_desc.type == ESP_CPU_INTR_TYPE_LEVEL)) || (((!(flags & ESP_INTR_FLAG_EDGE)) && (intr_desc.type == ESP_CPU_INTR_TYPE_EDGE)))) { INTC_LOG("....Unusable: incompatible trigger type"); return false; } #endif /* check if interrupt is reserved at runtime */ if (vd->flags & VECDESC_FL_RESERVED) { INTC_LOG("....Unusable: reserved at runtime."); return false; } /* Ints can't be both shared and non-shared. */ assert(!((vd->flags & VECDESC_FL_SHARED) && (vd->flags & VECDESC_FL_NONSHARED))); /* check if interrupt already is in use by a non-shared interrupt */ if (vd->flags & VECDESC_FL_NONSHARED) { INTC_LOG("....Unusable: already in (non-shared) use."); return false; } /* check shared interrupt flags */ if (vd->flags & VECDESC_FL_SHARED) { if (flags & ESP_INTR_FLAG_SHARED) { bool in_iram_flag = ((flags & ESP_INTR_FLAG_IRAM) != 0); bool desc_in_iram_flag = ((vd->flags & VECDESC_FL_INIRAM) != 0); /* * Bail out if int is shared, but iram property * doesn't match what we want. */ if ((vd->flags & VECDESC_FL_SHARED) && (desc_in_iram_flag != in_iram_flag)) { INTC_LOG("....Unusable: shared but iram prop doesn't match"); return false; } } else { /* * We need an unshared IRQ; can't use shared ones; * bail out if this is shared. */ INTC_LOG("...Unusable: int is shared, we need non-shared."); return false; } } else if (intr_has_handler(x, cpu)) { /* Check if interrupt already is allocated by set_interrupt_handler */ INTC_LOG("....Unusable: already allocated"); return false; } return true; } /* * Locate a free interrupt compatible with the flags given. * The 'force' argument can be -1, or 0-31 to force checking a certain interrupt. * When a CPU is forced, the INTDESC_SPECIAL marked interrupts are also accepted. */ static int get_available_int(int flags, int cpu, int force, int source) { int x; int best = -1; int best_level = 9; int best_shared_ct = INT_MAX; /* Default vector desc, for vectors not in the linked list */ struct vector_desc_t empty_vect_desc; memset(&empty_vect_desc, 0, sizeof(struct vector_desc_t)); /* Level defaults to any low/med interrupt */ if (!(flags & ESP_INTR_FLAG_LEVELMASK)) { flags |= ESP_INTR_FLAG_LOWMED; } INTC_LOG("%s: try to find existing. Cpu: %d, Source: %d", __func__, cpu, source); struct vector_desc_t *vd = find_desc_for_source(source, cpu); if (vd) { /* if existing vd found, don't need to search any more. */ INTC_LOG("%s: existing vd found. intno: %d", __func__, vd->intno); if (force != -1 && force != vd->intno) { INTC_LOG("%s: intr forced but not match existing. " "existing intno: %d, force: %d", __func__, vd->intno, force); } else if (!is_vect_desc_usable(vd, flags, cpu, force)) { INTC_LOG("%s: existing vd invalid.", __func__); } else { best = vd->intno; } return best; } if (force != -1) { INTC_LOG("%s: try to find force. " "Cpu: %d, Source: %d, Force: %d", __func__, cpu, source, force); /* if force assigned, don't need to search any more. */ vd = find_desc_for_int(force, cpu); if (vd == NULL) { /* if existing vd not found, just check the default state for the intr. */ empty_vect_desc.intno = force; vd = &empty_vect_desc; } if (is_vect_desc_usable(vd, flags, cpu, force)) { best = vd->intno; } else { INTC_LOG("%s: forced vd invalid.", __func__); } return best; } INTC_LOG("%s: start looking. Current cpu: %d", __func__, cpu); /* No allocated handlers as well as forced intr, iterate over the 32 possible interrupts */ for (x = 0; x < ESP_INTC_INTS_NUM; x++) { /* Grab the vector_desc for this vector. */ vd = find_desc_for_int(x, cpu); if (vd == NULL) { empty_vect_desc.intno = x; vd = &empty_vect_desc; } esp_cpu_intr_desc_t intr_desc; esp_cpu_intr_get_desc(cpu, x, &intr_desc); INTC_LOG("Int %d reserved %d level %d %s hasIsr %d", x, intr_desc.flags & ESP_CPU_INTR_DESC_FLAG_RESVD, intr_desc.priority, intr_desc.type == ESP_CPU_INTR_TYPE_LEVEL ? "LEVEL" : "EDGE", intr_has_handler(x, cpu)); if (!is_vect_desc_usable(vd, flags, cpu, force)) { continue; } if (flags & ESP_INTR_FLAG_SHARED) { /* We're allocating a shared int. */ /* See if int already is used as a shared interrupt. */ if (vd->flags & VECDESC_FL_SHARED) { /* * We can use this already-marked-as-shared interrupt. Count the * already attached isrs in order to see how useful it is. */ int no = 0; struct shared_vector_desc_t *svdesc = vd->shared_vec_info; while (svdesc != NULL) { no++; svdesc = svdesc->next; } if (no < best_shared_ct || best_level > intr_desc.priority) { /* * Seems like this shared vector is both okay and has * the least amount of ISRs already attached to it. */ best = x; best_shared_ct = no; best_level = intr_desc.priority; INTC_LOG("...int %d more usable as a shared int: " "has %d existing vectors", x, no); } else { INTC_LOG("...worse than int %d", best); } } else { if (best == -1) { /* * We haven't found a feasible shared interrupt yet. * This one is still free and usable, even if not * marked as shared. * Remember it in case we don't find any other shared * interrupt that qualifies. */ if (best_level > intr_desc.priority) { best = x; best_level = intr_desc.priority; INTC_LOG("...int %d usable as new shared int", x); } } else { INTC_LOG("...already have a shared int"); } } } else { /* * Seems this interrupt is feasible. Select it and break out of the loop * No need to search further. */ if (best_level > intr_desc.priority) { best = x; best_level = intr_desc.priority; } else { INTC_LOG("...worse than int %d", best); } } } INTC_LOG("%s: using int %d", __func__, best); /* * By now we have looked at all potential interrupts and * hopefully have selected the best one in best. */ return best; } /* Common shared isr handler. Chain-call all ISRs. */ static void IRAM_ATTR shared_intr_isr(void *arg) { struct vector_desc_t *vd = (struct vector_desc_t *)arg; struct shared_vector_desc_t *sh_vec = vd->shared_vec_info; esp_intr_lock(); while (sh_vec) { if (!sh_vec->disabled) { if ((sh_vec->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) { sh_vec->isr(sh_vec->arg); } } sh_vec = sh_vec->next; } esp_intr_unlock(); } int esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, struct intr_handle_data_t **ret_handle) { struct intr_handle_data_t *ret = NULL; int force = -1; INTC_LOG("%s (cpu %d): checking args", __func__, esp_cpu_get_core_id()); /* Shared interrupts should be level-triggered. */ if ((flags & ESP_INTR_FLAG_SHARED) && (flags & ESP_INTR_FLAG_EDGE)) { return -EINVAL; } /* You can't set an handler / arg for a non-C-callable interrupt. */ if ((flags & ESP_INTR_FLAG_HIGH) && (handler)) { return -EINVAL; } /* Shared ints should have handler and non-processor-local source */ if ((flags & ESP_INTR_FLAG_SHARED) && (!handler || source < 0)) { return -EINVAL; } /* Statusreg should have a mask */ if (intrstatusreg && !intrstatusmask) { return -EINVAL; } /* * If the ISR is marked to be IRAM-resident, the handler must not be in the cached region * If we are to allow placing interrupt handlers into the 0x400c0000—0x400c2000 region, * we need to make sure the interrupt is connected to the CPU0. * CPU1 does not have access to the RTC fast memory through this region. */ if ((flags & ESP_INTR_FLAG_IRAM) && handler && !esp_ptr_in_iram(handler) && !esp_ptr_in_rtc_iram_fast(handler)) { return -EINVAL; } /* * Default to prio 1 for shared interrupts. * Default to prio 1, 2 or 3 for non-shared interrupts. */ if ((flags & ESP_INTR_FLAG_LEVELMASK) == 0) { if (flags & ESP_INTR_FLAG_SHARED) { flags |= ESP_INTR_FLAG_LEVEL1; } else { flags |= ESP_INTR_FLAG_LOWMED; } } INTC_LOG("%s (cpu %d): Args okay." "Resulting flags 0x%X", __func__, esp_cpu_get_core_id(), flags); /* * Check 'special' interrupt sources. These are tied to one specific * interrupt, so we have to force get_available_int to only look at that. */ switch (source) { case ETS_INTERNAL_TIMER0_INTR_SOURCE: force = ETS_INTERNAL_TIMER0_INTR_NO; break; case ETS_INTERNAL_TIMER1_INTR_SOURCE: force = ETS_INTERNAL_TIMER1_INTR_NO; break; case ETS_INTERNAL_TIMER2_INTR_SOURCE: force = ETS_INTERNAL_TIMER2_INTR_NO; break; case ETS_INTERNAL_SW0_INTR_SOURCE: force = ETS_INTERNAL_SW0_INTR_NO; break; case ETS_INTERNAL_SW1_INTR_SOURCE: force = ETS_INTERNAL_SW1_INTR_NO; break; case ETS_INTERNAL_PROFILING_INTR_SOURCE: force = ETS_INTERNAL_PROFILING_INTR_NO; break; default: break; } /* Allocate a return handle. If we end up not needing it, we'll free it later on. */ ret = k_malloc(sizeof(struct intr_handle_data_t)); if (ret == NULL) { return -ENOMEM; } esp_intr_lock(); int cpu = esp_cpu_get_core_id(); /* See if we can find an interrupt that matches the flags. */ int intr = get_available_int(flags, cpu, force, source); if (intr == -1) { /* None found. Bail out. */ esp_intr_unlock(); k_free(ret); return -ENODEV; } /* Get an int vector desc for int. */ struct vector_desc_t *vd = get_desc_for_int(intr, cpu); if (vd == NULL) { esp_intr_unlock(); k_free(ret); return -ENOMEM; } /* Allocate that int! */ if (flags & ESP_INTR_FLAG_SHARED) { /* Populate vector entry and add to linked list. */ struct shared_vector_desc_t *sv = k_malloc(sizeof(struct shared_vector_desc_t)); if (sv == NULL) { esp_intr_unlock(); k_free(ret); return -ENOMEM; } memset(sv, 0, sizeof(struct shared_vector_desc_t)); sv->statusreg = (uint32_t *)intrstatusreg; sv->statusmask = intrstatusmask; sv->isr = handler; sv->arg = arg; sv->next = vd->shared_vec_info; sv->source = source; sv->disabled = 0; vd->shared_vec_info = sv; vd->flags |= VECDESC_FL_SHARED; /* (Re-)set shared isr handler to new value. */ set_interrupt_handler(intr, shared_intr_isr, vd); } else { /* Mark as unusable for other interrupt sources. This is ours now! */ vd->flags = VECDESC_FL_NONSHARED; if (handler) { set_interrupt_handler(intr, handler, arg); } if (flags & ESP_INTR_FLAG_EDGE) { xthal_set_intclear(1 << intr); } vd->source = source; } if (flags & ESP_INTR_FLAG_IRAM) { vd->flags |= VECDESC_FL_INIRAM; non_iram_int_mask[cpu] &= ~(1 << intr); } else { vd->flags &= ~VECDESC_FL_INIRAM; non_iram_int_mask[cpu] |= (1 << intr); } if (source >= 0) { esp_rom_route_intr_matrix(cpu, source, intr); } /* Fill return handle data. */ ret->vector_desc = vd; ret->shared_vector_desc = vd->shared_vec_info; /* Enable int at CPU-level; */ irq_enable(intr); /* * If interrupt has to be started disabled, do that now; ints won't be enabled for * real until the end of the critical section. */ if (flags & ESP_INTR_FLAG_INTRDISABLED) { esp_intr_disable(ret); } #ifdef SOC_CPU_HAS_FLEXIBLE_INTC /* Extract the level from the interrupt passed flags */ int level = esp_intr_flags_to_level(flags); esp_cpu_intr_set_priority(intr, level); if (flags & ESP_INTR_FLAG_EDGE) { esp_cpu_intr_set_type(intr, ESP_CPU_INTR_TYPE_EDGE); } else { esp_cpu_intr_set_type(intr, ESP_CPU_INTR_TYPE_LEVEL); } #endif esp_intr_unlock(); /* Fill return handle if needed, otherwise free handle. */ if (ret_handle != NULL) { *ret_handle = ret; } else { k_free(ret); } LOG_DBG("Connected src %d to int %d (cpu %d)", source, intr, cpu); return 0; } int esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, struct intr_handle_data_t **ret_handle) { /* * As an optimization, we can create a table with the possible interrupt status * registers and masks for every single source there is. We can then add code here to * look up an applicable value and pass that to the esp_intr_alloc_intrstatus function. */ return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle); } int IRAM_ATTR esp_intr_set_in_iram(struct intr_handle_data_t *handle, bool is_in_iram) { if (!handle) { return -EINVAL; } struct vector_desc_t *vd = handle->vector_desc; if (vd->flags & VECDESC_FL_SHARED) { return -EINVAL; } esp_intr_lock(); uint32_t mask = (1 << vd->intno); if (is_in_iram) { vd->flags |= VECDESC_FL_INIRAM; non_iram_int_mask[vd->cpu] &= ~mask; } else { vd->flags &= ~VECDESC_FL_INIRAM; non_iram_int_mask[vd->cpu] |= mask; } esp_intr_unlock(); return 0; } int esp_intr_free(struct intr_handle_data_t *handle) { bool free_shared_vector = false; if (!handle) { return -EINVAL; } esp_intr_lock(); esp_intr_disable(handle); if (handle->vector_desc->flags & VECDESC_FL_SHARED) { /* Find and kill the shared int */ struct shared_vector_desc_t *svd = handle->vector_desc->shared_vec_info; struct shared_vector_desc_t *prevsvd = NULL; assert(svd); /* should be something in there for a shared int */ while (svd != NULL) { if (svd == handle->shared_vector_desc) { /* Found it. Now kill it. */ if (prevsvd) { prevsvd->next = svd->next; } else { handle->vector_desc->shared_vec_info = svd->next; } k_free(svd); break; } prevsvd = svd; svd = svd->next; } /* If nothing left, disable interrupt. */ if (handle->vector_desc->shared_vec_info == NULL) { free_shared_vector = true; } INTC_LOG("%s: Deleting shared int: %s. " "Shared int is %s", __func__, svd ? "not found or last one" : "deleted", free_shared_vector ? "empty now." : "still in use"); } if ((handle->vector_desc->flags & VECDESC_FL_NONSHARED) || free_shared_vector) { INTC_LOG("%s: Disabling int, killing handler", __func__); /* Reset to normal handler */ set_interrupt_handler(handle->vector_desc->intno, default_intr_handler, (void *)((int)handle->vector_desc->intno)); /* * Theoretically, we could free the vector_desc... not sure if that's worth the * few bytes of memory we save.(We can also not use the same exit path for empty * shared ints anymore if we delete the desc.) For now, just mark it as free. */ handle->vector_desc->flags &= ~(VECDESC_FL_NONSHARED | VECDESC_FL_RESERVED | VECDESC_FL_SHARED); /* Also kill non_iram mask bit. */ non_iram_int_mask[handle->vector_desc->cpu] &= ~(1 << (handle->vector_desc->intno)); } esp_intr_unlock(); k_free(handle); return 0; } int esp_intr_get_intno(struct intr_handle_data_t *handle) { return handle->vector_desc->intno; } int esp_intr_get_cpu(struct intr_handle_data_t *handle) { return handle->vector_desc->cpu; } /** * Interrupt disabling strategy: * If the source is >=0 (meaning a muxed interrupt), we disable it by muxing the interrupt to a * non-connected interrupt. If the source is <0 (meaning an internal, per-cpu interrupt). * This allows us to, for the muxed CPUs, disable an int from * the other core. It also allows disabling shared interrupts. */ /* * Muxing an interrupt source to interrupt 6, 7, 11, 15, 16 or 29 * cause the interrupt to effectively be disabled. */ #define INT_MUX_DISABLED_INTNO 6 int IRAM_ATTR esp_intr_enable(struct intr_handle_data_t *handle) { if (!handle) { return -EINVAL; } esp_intr_lock(); int source; if (handle->shared_vector_desc) { handle->shared_vector_desc->disabled = 0; source = handle->shared_vector_desc->source; } else { source = handle->vector_desc->source; } if (source >= 0) { /* Disabled using int matrix; re-connect to enable */ esp_rom_route_intr_matrix(handle->vector_desc->cpu, source, handle->vector_desc->intno); } else { /* Re-enable using cpu int ena reg */ if (handle->vector_desc->cpu != esp_cpu_get_core_id()) { esp_intr_unlock(); return -EINVAL; /* Can only enable these ints on this cpu */ } irq_enable(handle->vector_desc->intno); } esp_intr_unlock(); return 0; } int IRAM_ATTR esp_intr_disable(struct intr_handle_data_t *handle) { if (!handle) { return -EINVAL; } esp_intr_lock(); int source; bool disabled = 1; if (handle->shared_vector_desc) { handle->shared_vector_desc->disabled = 1; source = handle->shared_vector_desc->source; struct shared_vector_desc_t *svd = handle->vector_desc->shared_vec_info; assert(svd != NULL); while (svd) { if (svd->source == source && svd->disabled == 0) { disabled = 0; break; } svd = svd->next; } } else { source = handle->vector_desc->source; } if (source >= 0) { if (disabled) { /* Disable using int matrix */ esp_rom_route_intr_matrix(handle->vector_desc->cpu, source, INT_MUX_DISABLED_INTNO); } } else { /* Disable using per-cpu regs */ if (handle->vector_desc->cpu != esp_cpu_get_core_id()) { esp_intr_unlock(); return -EINVAL; /* Can only enable these ints on this cpu */ } irq_disable(handle->vector_desc->intno); } esp_intr_unlock(); return 0; } void IRAM_ATTR esp_intr_noniram_disable(void) { esp_intr_lock(); int oldint; int cpu = esp_cpu_get_core_id(); int non_iram_ints = ~non_iram_int_mask[cpu]; if (non_iram_int_disabled_flag[cpu]) { abort(); } non_iram_int_disabled_flag[cpu] = true; oldint = esp_cpu_intr_get_enabled_mask(); esp_cpu_intr_disable(non_iram_ints); rtc_isr_noniram_disable(cpu); non_iram_int_disabled[cpu] = oldint & non_iram_ints; esp_intr_unlock(); } void IRAM_ATTR esp_intr_noniram_enable(void) { esp_intr_lock(); int cpu = esp_cpu_get_core_id(); int non_iram_ints = non_iram_int_disabled[cpu]; if (!non_iram_int_disabled_flag[cpu]) { abort(); } non_iram_int_disabled_flag[cpu] = false; esp_cpu_intr_enable(non_iram_ints); rtc_isr_noniram_enable(cpu); esp_intr_unlock(); }