/* * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include "soc/soc.h" #include #include #include #include #include #define ESP32C3_INTC_DEFAULT_PRIO 15 #include LOG_MODULE_REGISTER(intc_esp32c3, CONFIG_LOG_DEFAULT_LEVEL); /* * 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_ESP32C3_DECISIONS_LOG # define INTC_LOG(...) LOG_INF(__VA_ARGS__) #else # define INTC_LOG(...) do {} while (0) #endif #define ESP32C3_INTC_DEFAULT_PRIORITY 15 #define ESP32C3_INTC_DEFAULT_THRESHOLD 1 #define ESP32C3_INTC_DISABLED_SLOT 31 #define ESP32C3_INTC_SRCS_PER_IRQ 2 #define ESP32C3_INTC_AVAILABLE_IRQS 30 #if defined(CONFIG_SOC_SERIES_ESP32C6) #define IRQ_NA 0xFF /* IRQ not available */ #define IRQ_FREE 0xFE #define ESP32C6_INTC_SRCS_PER_IRQ 2 #define ESP32C6_INTC_AVAILABLE_IRQS 31 /* Interrupt overview for ESP32C6: * - 0, 3, 4, and 7 are used by the CPU for core-local interrupts (CLINT) * - 1 is used for Wi-Fi in Espressif HAL * - 2, 5, 6, 8 .. 31 are available for Zephyr * - 31 is reserved for disabled interrupts */ static uint8_t esp_intr_irq_alloc[ESP32C6_INTC_AVAILABLE_IRQS][ESP32C6_INTC_SRCS_PER_IRQ] = { [0] = {IRQ_NA, IRQ_NA}, [1] = {IRQ_NA, IRQ_NA}, [2] = {IRQ_FREE, IRQ_FREE}, [3] = {IRQ_NA, IRQ_NA}, [4] = {IRQ_NA, IRQ_NA}, [5 ... 6] = {IRQ_FREE, IRQ_FREE}, [7] = {IRQ_NA, IRQ_NA}, [8 ... 30] = {IRQ_FREE, IRQ_FREE} }; #endif #define STATUS_MASK_NUM 3 static uint32_t esp_intr_enabled_mask[STATUS_MASK_NUM] = {0, 0, 0}; #if defined(CONFIG_SOC_SERIES_ESP32C2) || defined(CONFIG_SOC_SERIES_ESP32C3) static uint32_t esp_intr_find_irq_for_source(uint32_t source) { /* in general case, each 2 sources goes routed to * 1 IRQ line. */ uint32_t irq = (source / ESP32C3_INTC_SRCS_PER_IRQ); if (irq > ESP32C3_INTC_AVAILABLE_IRQS) { INTC_LOG("Clamping the source: %d no more IRQs available", source); irq = ESP32C3_INTC_AVAILABLE_IRQS; } else if (irq == 0) { irq = 1; } INTC_LOG("Found IRQ: %d for source: %d", irq, source); return irq; } #elif defined(CONFIG_SOC_SERIES_ESP32C6) static uint32_t esp_intr_find_irq_for_source(uint32_t source) { uint32_t irq = IRQ_NA; uint32_t irq_free = IRQ_NA; uint8_t *irq_ptr = NULL; /* First allocate one source per IRQ, then two * if there are more sources than free IRQs */ for (int j = 0; j < ESP32C6_INTC_SRCS_PER_IRQ; j++) { for (int i = 0; i < ESP32C6_INTC_AVAILABLE_IRQS; i++) { /* Find first free slot but keep searching to see * if source is already associated to an IRQ */ if (esp_intr_irq_alloc[i][j] == source) { /* Source is already associated to an IRQ */ irq = i; goto found; } else if ((irq_free == IRQ_NA) && (esp_intr_irq_alloc[i][j] == IRQ_FREE)) { irq_free = i; irq_ptr = &esp_intr_irq_alloc[i][j]; } } } if (irq_ptr != NULL) { *irq_ptr = (uint8_t)source; irq = irq_free; } else { return IRQ_NA; } found: INTC_LOG("Found IRQ: %d for source: %d", irq, source); return irq; } #endif void esp_intr_initialize(void) { /* IRQ 31 is reserved for disabled interrupts, * so route all sources to it */ for (int i = 0 ; i < ESP32C3_INTC_AVAILABLE_IRQS + 2; i++) { irq_disable(i); } for (int i = 0; i < ETS_MAX_INTR_SOURCE; i++) { esp_rom_intr_matrix_set(0, i, ESP32C3_INTC_DISABLED_SLOT); } #if defined(CONFIG_SOC_SERIES_ESP32C6) /* Clear up IRQ allocation */ for (int j = 0; j < ESP32C6_INTC_SRCS_PER_IRQ; j++) { for (int i = 0; i < ESP32C6_INTC_AVAILABLE_IRQS; i++) { /* screen out reserved IRQs */ if (esp_intr_irq_alloc[i][j] != IRQ_NA) { esp_intr_irq_alloc[i][j] = IRQ_FREE; } } } #endif /* set global esp32c3's INTC masking level */ esprv_intc_int_set_threshold(ESP32C3_INTC_DEFAULT_THRESHOLD); } int esp_intr_alloc(int source, int flags, isr_handler_t handler, void *arg, void **ret_handle) { ARG_UNUSED(flags); ARG_UNUSED(ret_handle); if (handler == NULL) { return -EINVAL; } if (source < 0 || source >= ETS_MAX_INTR_SOURCE) { return -EINVAL; } uint32_t key = irq_lock(); irq_connect_dynamic(source, ESP32C3_INTC_DEFAULT_PRIORITY, handler, arg, 0); if (source < 32) { esp_intr_enabled_mask[0] |= (1 << source); } else if (source < 64) { esp_intr_enabled_mask[1] |= (1 << (source - 32)); } else if (source < 96) { esp_intr_enabled_mask[2] |= (1 << (source - 64)); } INTC_LOG("Enabled ISRs -- 0: 0x%X -- 1: 0x%X -- 2: 0x%X", esp_intr_enabled_mask[0], esp_intr_enabled_mask[1], esp_intr_enabled_mask[2]); irq_unlock(key); int ret = esp_intr_enable(source); return ret; } int esp_intr_disable(int source) { if (source < 0 || source >= ETS_MAX_INTR_SOURCE) { return -EINVAL; } uint32_t key = irq_lock(); esp_rom_intr_matrix_set(0, source, ESP32C3_INTC_DISABLED_SLOT); #if defined(CONFIG_SOC_SERIES_ESP32C6) for (int j = 0; j < ESP32C6_INTC_SRCS_PER_IRQ; j++) { for (int i = 0; i < ESP32C6_INTC_AVAILABLE_IRQS; i++) { if (esp_intr_irq_alloc[i][j] == source) { esp_intr_irq_alloc[i][j] = IRQ_FREE; goto freed; } } } freed: #endif if (source < 32) { esp_intr_enabled_mask[0] &= ~(1 << source); } else if (source < 64) { esp_intr_enabled_mask[1] &= ~(1 << (source - 32)); } else if (source < 96) { esp_intr_enabled_mask[2] &= ~(1 << (source - 64)); } INTC_LOG("Enabled ISRs -- 0: 0x%X -- 1: 0x%X -- 2: 0x%X", esp_intr_enabled_mask[0], esp_intr_enabled_mask[1], esp_intr_enabled_mask[2]); irq_unlock(key); return 0; } int esp_intr_enable(int source) { if (source < 0 || source >= ETS_MAX_INTR_SOURCE) { return -EINVAL; } uint32_t key = irq_lock(); uint32_t irq = esp_intr_find_irq_for_source(source); #if defined(CONFIG_SOC_SERIES_ESP32C6) if (irq == IRQ_NA) { irq_unlock(key); return -ENOMEM; } #endif esp_rom_intr_matrix_set(0, source, irq); if (source < 32) { esp_intr_enabled_mask[0] |= (1 << source); } else if (source < 64) { esp_intr_enabled_mask[1] |= (1 << (source - 32)); } else if (source < 96) { esp_intr_enabled_mask[2] |= (1 << (source - 64)); } INTC_LOG("Enabled ISRs -- 0: 0x%X -- 1: 0x%X -- 2: 0x%X", esp_intr_enabled_mask[0], esp_intr_enabled_mask[1], esp_intr_enabled_mask[2]); esprv_intc_int_set_priority(irq, ESP32C3_INTC_DEFAULT_PRIO); esprv_intc_int_set_type(irq, INTR_TYPE_LEVEL); esprv_intc_int_enable(1 << irq); irq_unlock(key); return 0; } uint32_t esp_intr_get_enabled_intmask(int status_mask_number) { INTC_LOG("Enabled ISRs -- 0: 0x%X -- 1: 0x%X -- 2: 0x%X", esp_intr_enabled_mask[0], esp_intr_enabled_mask[1], esp_intr_enabled_mask[2]); if (status_mask_number < STATUS_MASK_NUM) { return esp_intr_enabled_mask[status_mask_number]; } else { return 0; /* error */ } }