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 <sylvio.alves@espressif.com>
This commit is contained in:
parent
fff500469e
commit
f22de9733b
3 changed files with 118 additions and 132 deletions
|
@ -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 <zephyr/sw_isr_table.h>
|
||||
#include <riscv/interrupt.h>
|
||||
|
||||
#define ESP32C3_INTC_DEFAULT_PRIO 15
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue