From f22de9733b94f1c1f335934664f649b9a80f38bf Mon Sep 17 00:00:00 2001 From: Sylvio Alves Date: Sat, 1 Mar 2025 16:24:33 -0300 Subject: [PATCH] soc: esp32: riscv: fix interrupt allocator Current interrupt allocator is not taking into account reserved areas. In case of esp32c6, Wi-Fi isn't properly configured, causing instability or even non-functional feature. This adds the reserved area ranges for all risc-v based SoC and unify the slot finding based on interrupt source. Signed-off-by: Sylvio Alves --- drivers/interrupt_controller/intc_esp32c3.c | 241 ++++++++---------- .../interrupt_controller/intc_esp32c3.h | 3 + soc/espressif/esp32c6/soc_irq.c | 6 +- 3 files changed, 118 insertions(+), 132 deletions(-) diff --git a/drivers/interrupt_controller/intc_esp32c3.c b/drivers/interrupt_controller/intc_esp32c3.c index da889a8f0af..4ddfbfcb132 100644 --- a/drivers/interrupt_controller/intc_esp32c3.c +++ b/drivers/interrupt_controller/intc_esp32c3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * Copyright (c) 2021-2025 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ @@ -19,10 +19,8 @@ #include #include -#define ESP32C3_INTC_DEFAULT_PRIO 15 - #include -LOG_MODULE_REGISTER(intc_esp32c3, CONFIG_LOG_DEFAULT_LEVEL); +LOG_MODULE_REGISTER(intc_esp32, CONFIG_LOG_DEFAULT_LEVEL); /* * Define this to debug the choices made when allocating the interrupt. This leads to much debugging @@ -35,132 +33,118 @@ LOG_MODULE_REGISTER(intc_esp32c3, CONFIG_LOG_DEFAULT_LEVEL); # 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 +#define ESP32_INTC_DEFAULT_PRIORITY 15 +#define ESP32_INTC_DEFAULT_THRESHOLD 1 +#define ESP32_INTC_DISABLED_SLOT 31 +#define ESP32_INTC_SRCS_PER_IRQ 2 +/* Define maximum interrupt sources per SoC */ #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 +/* + * Interrupt reserved mask + * 0 is reserved + * 1 is for Wi-Fi + * 3, 4 and 7 are unavailable for PULP CPU as they are bound to Core-Local Interrupts (CLINT) */ -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} -}; +#define RSVD_MASK (BIT(0) | BIT(1) | BIT(3) | BIT(4) | BIT(7)) +#define ESP_INTC_AVAILABLE_IRQS 31 +#else +/* + * Interrupt reserved mask + * 1 is for Wi-Fi + */ +#define RSVD_MASK (BIT(0) | BIT(1)) +#define ESP_INTC_AVAILABLE_IRQS 30 #endif -#define STATUS_MASK_NUM 3 +/* Single array for IRQ allocation */ +static uint8_t esp_intr_irq_alloc[ESP_INTC_AVAILABLE_IRQS * ESP32_INTC_SRCS_PER_IRQ]; + +#define ESP_INTR_IDX(irq, slot) ((irq % ESP_INTC_AVAILABLE_IRQS) * ESP32_INTC_SRCS_PER_IRQ + slot) + +#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 { + if (source >= ETS_MAX_INTR_SOURCE) { return IRQ_NA; } -found: - INTC_LOG("Found IRQ: %d for source: %d", irq, source); + uint32_t irq = source / ESP32_INTC_SRCS_PER_IRQ; - return irq; -} + /* Check if the derived IRQ is usable first */ + for (int j = 0; j < ESP32_INTC_SRCS_PER_IRQ; j++) { + int idx = ESP_INTR_IDX(irq, j); -#endif + /* Ensure idx is within a valid range */ + if (idx >= ARRAY_SIZE(esp_intr_irq_alloc)) { + continue; + } -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); + /* If source is already assigned, return the IRQ */ + if (esp_intr_irq_alloc[idx] == source) { + return irq; + } + + /* If slot is free, allocate it */ + if (esp_intr_irq_alloc[idx] == IRQ_FREE) { + esp_intr_irq_alloc[idx] = source; + return irq; + } } - for (int i = 0; i < ETS_MAX_INTR_SOURCE; i++) { - esp_rom_intr_matrix_set(0, i, ESP32C3_INTC_DISABLED_SLOT); - } + /* If derived IRQ is full, search for another available IRQ */ + for (irq = 0; irq < ESP_INTC_AVAILABLE_IRQS; irq++) { + if (RSVD_MASK & (1U << irq)) { + continue; + } + for (int j = 0; j < ESP32_INTC_SRCS_PER_IRQ; j++) { + int idx = ESP_INTR_IDX(irq, j); -#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; + /* Ensure idx is within a valid range */ + if (idx >= ARRAY_SIZE(esp_intr_irq_alloc)) { + continue; + } + + /* If source is already assigned, return this IRQ */ + if (esp_intr_irq_alloc[idx] == source) { + return irq; + } + + /* If slot is free, allocate it */ + if (esp_intr_irq_alloc[idx] == IRQ_FREE) { + esp_intr_irq_alloc[idx] = source; + return irq; } } } -#endif - /* set global esp32c3's INTC masking level */ - esprv_intc_int_set_threshold(ESP32C3_INTC_DEFAULT_THRESHOLD); + /* No available slot found */ + return IRQ_NA; +} + +void esp_intr_initialize(void) +{ + for (int i = 0; i < ETS_MAX_INTR_SOURCE; i++) { + esp_rom_intr_matrix_set(0, i, ESP32_INTC_DISABLED_SLOT); + } + + for (int irq = 0; irq < ESP_INTC_AVAILABLE_IRQS; irq++) { + for (int j = 0; j < ESP32_INTC_SRCS_PER_IRQ; j++) { + int idx = ESP_INTR_IDX(irq, j); + + if (RSVD_MASK & (1U << irq)) { + esp_intr_irq_alloc[idx] = IRQ_NA; + } else { + esp_intr_irq_alloc[idx] = IRQ_FREE; + } + } + } + + /* set global INTC masking level */ + esprv_intc_int_set_threshold(ESP32_INTC_DEFAULT_THRESHOLD); } int esp_intr_alloc(int source, @@ -181,21 +165,19 @@ int esp_intr_alloc(int source, } uint32_t key = irq_lock(); + uint32_t irq = esp_intr_find_irq_for_source(source); + + if (irq == IRQ_NA) { + irq_unlock(key); + return -ENOMEM; + } irq_connect_dynamic(source, - ESP32C3_INTC_DEFAULT_PRIORITY, + ESP32_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]); @@ -215,19 +197,20 @@ int esp_intr_disable(int source) esp_rom_intr_matrix_set(0, source, - ESP32C3_INTC_DISABLED_SLOT); + ESP32_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; + for (int i = 0; i < ESP_INTC_AVAILABLE_IRQS; i++) { + if (RSVD_MASK & (1U << i)) { + continue; + } + for (int j = 0; j < ESP32_INTC_SRCS_PER_IRQ; j++) { + int idx = ESP_INTR_IDX(i, j); + + if (esp_intr_irq_alloc[idx] == source) { + esp_intr_irq_alloc[idx] = IRQ_FREE; } } } -freed: -#endif if (source < 32) { esp_intr_enabled_mask[0] &= ~(1 << source); @@ -254,12 +237,10 @@ int esp_intr_enable(int source) 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); @@ -274,7 +255,7 @@ int esp_intr_enable(int source) 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_priority(irq, ESP32_INTC_DEFAULT_PRIORITY); esprv_intc_int_set_type(irq, INTR_TYPE_LEVEL); esprv_intc_int_enable(1 << irq); @@ -290,7 +271,7 @@ uint32_t esp_intr_get_enabled_intmask(int status_mask_number) if (status_mask_number < STATUS_MASK_NUM) { return esp_intr_enabled_mask[status_mask_number]; - } else { - return 0; /* error */ } + + return 0; } diff --git a/include/zephyr/drivers/interrupt_controller/intc_esp32c3.h b/include/zephyr/drivers/interrupt_controller/intc_esp32c3.h index a30dc16ed0f..127e7972632 100644 --- a/include/zephyr/drivers/interrupt_controller/intc_esp32c3.h +++ b/include/zephyr/drivers/interrupt_controller/intc_esp32c3.h @@ -40,6 +40,9 @@ ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6| \ ESP_INTR_FLAG_NMI) +#define IRQ_NA 0xFF /* IRQ not available */ +#define IRQ_FREE 0xFE /* IRQ available for use */ + /* * Get the interrupt flags from the supplied priority. */ diff --git a/soc/espressif/esp32c6/soc_irq.c b/soc/espressif/esp32c6/soc_irq.c index 6a452ec78b9..b048b5f325a 100644 --- a/soc/espressif/esp32c6/soc_irq.c +++ b/soc/espressif/esp32c6/soc_irq.c @@ -53,7 +53,7 @@ int arch_irq_is_enabled(unsigned int irq) uint32_t soc_intr_get_next_source(void) { uint32_t status; - uint32_t source; + uint32_t source = IRQ_NA; /* Status register for interrupt sources 0 ~ 31 */ status = REG_READ(INTMTX_CORE0_INT_STATUS_REG_0_REG) & @@ -77,7 +77,9 @@ uint32_t soc_intr_get_next_source(void) status = REG_READ(INTMTX_CORE0_INT_STATUS_REG_2_REG) & esp_intr_get_enabled_intmask(2); - source = (__builtin_ffs(status) - 1 + ESP32C6_INTSTATUS_REG2_THRESHOLD); + if (status) { + source = (__builtin_ffs(status) - 1 + ESP32C6_INTSTATUS_REG2_THRESHOLD); + } ret: return source;