Browse Source
Update interrupt allocator to use the same driver for both Xtensa and RISCV devices. Signed-off-by: Raffael Rostagno <raffael.rostagno@espressif.com> Signed-off-by: Sylvio Alves <sylvio.alves@espressif.com>pull/87800/merge
14 changed files with 107 additions and 552 deletions
@ -1,21 +0,0 @@
@@ -1,21 +0,0 @@
|
||||
# Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
config INTC_ESP32C3 |
||||
bool "ESP32C3 interrupt controller driver" |
||||
depends on SOC_FAMILY_ESPRESSIF_ESP32 |
||||
depends on SOC_SERIES_ESP32C2 || SOC_SERIES_ESP32C3 || SOC_SERIES_ESP32C6 |
||||
default y |
||||
help |
||||
Enables the esp32c3 interrupt controller driver to handle ISR |
||||
management at SoC level. |
||||
|
||||
config INTC_ESP32C3_DECISIONS_LOG |
||||
bool "Espressif's interrupt allocator logging" |
||||
depends on INTC_ESP32C3 |
||||
select LOG |
||||
help |
||||
Enable this option to visualize information on decisions made by the |
||||
interrupt allocator. This has no impact on the interrupt allocator usage |
||||
but may be valuable for debugging purposes. When enabled, messages are |
||||
print to the serial console. |
@ -1,277 +0,0 @@
@@ -1,277 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2025 Espressif Systems (Shanghai) Co., Ltd. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <stdint.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdbool.h> |
||||
#include <string.h> |
||||
#include <soc/periph_defs.h> |
||||
#include <limits.h> |
||||
#include <assert.h> |
||||
#include "soc/soc.h" |
||||
#include <soc.h> |
||||
#include <zephyr/kernel.h> |
||||
#include <zephyr/drivers/interrupt_controller/intc_esp32c3.h> |
||||
#include <zephyr/sw_isr_table.h> |
||||
#include <riscv/interrupt.h> |
||||
|
||||
#include <zephyr/logging/log.h> |
||||
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 |
||||
* 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 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) |
||||
/*
|
||||
* 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) |
||||
*/ |
||||
#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 |
||||
|
||||
/* 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}; |
||||
|
||||
static uint32_t esp_intr_find_irq_for_source(uint32_t source) |
||||
{ |
||||
if (source >= ETS_MAX_INTR_SOURCE) { |
||||
return IRQ_NA; |
||||
} |
||||
|
||||
uint32_t irq = source / ESP32_INTC_SRCS_PER_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); |
||||
|
||||
/* Ensure idx is within a valid range */ |
||||
if (idx >= ARRAY_SIZE(esp_intr_irq_alloc)) { |
||||
continue; |
||||
} |
||||
|
||||
/* 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; |
||||
} |
||||
} |
||||
|
||||
/* 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); |
||||
|
||||
/* 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; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* 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, |
||||
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(); |
||||
uint32_t irq = esp_intr_find_irq_for_source(source); |
||||
|
||||
if (irq == IRQ_NA) { |
||||
irq_unlock(key); |
||||
return -ENOMEM; |
||||
} |
||||
|
||||
irq_connect_dynamic(source, |
||||
ESP32_INTC_DEFAULT_PRIORITY, |
||||
handler, |
||||
arg, |
||||
0); |
||||
|
||||
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, |
||||
ESP32_INTC_DISABLED_SLOT); |
||||
|
||||
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; |
||||
} |
||||
} |
||||
} |
||||
|
||||
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 (irq == IRQ_NA) { |
||||
irq_unlock(key); |
||||
return -ENOMEM; |
||||
} |
||||
|
||||
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, ESP32_INTC_DEFAULT_PRIORITY); |
||||
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]; |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -1,124 +0,0 @@
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_INTERRUPT_CONTROLLER_INTC_ESP32C3_H_ |
||||
#define ZEPHYR_INCLUDE_DRIVERS_INTERRUPT_CONTROLLER_INTC_ESP32C3_H_ |
||||
|
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
#include <soc.h> |
||||
/*
|
||||
* Interrupt allocation flags - These flags can be used to specify |
||||
* which interrupt qualities the code calling esp_intr_alloc* needs. |
||||
*/ |
||||
|
||||
/* Keep the LEVELx values as they are here; they match up with (1<<level) */ |
||||
#define ESP_INTR_FLAG_LEVEL1 (1<<1) /* Accept a Level 1 int vector, lowest priority */ |
||||
#define ESP_INTR_FLAG_LEVEL2 (1<<2) /* Accept a Level 2 int vector */ |
||||
#define ESP_INTR_FLAG_LEVEL3 (1<<3) /* Accept a Level 3 int vector */ |
||||
#define ESP_INTR_FLAG_LEVEL4 (1<<4) /* Accept a Level 4 int vector */ |
||||
#define ESP_INTR_FLAG_LEVEL5 (1<<5) /* Accept a Level 5 int vector */ |
||||
#define ESP_INTR_FLAG_LEVEL6 (1<<6) /* Accept a Level 6 int vector */ |
||||
#define ESP_INTR_FLAG_NMI (1<<7) /* Accept a Level 7 int vector, highest priority */ |
||||
#define ESP_INTR_FLAG_SHARED (1<<8) /* Interrupt can be shared between ISRs */ |
||||
#define ESP_INTR_FLAG_EDGE (1<<9) /* Edge-triggered interrupt */ |
||||
#define ESP_INTR_FLAG_IRAM (1<<10) /* ISR can be called if cache is disabled */ |
||||
#define ESP_INTR_FLAG_INTRDISABLED (1<<11) /* Return with this interrupt disabled */ |
||||
|
||||
/* Low and medium prio interrupts. These can be handled in C. */ |
||||
#define ESP_INTR_FLAG_LOWMED (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3) |
||||
|
||||
/* High level interrupts. Need to be handled in assembly. */ |
||||
#define ESP_INTR_FLAG_HIGH (ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6| \ |
||||
ESP_INTR_FLAG_NMI) |
||||
|
||||
/* Mask for all level flags */ |
||||
#define ESP_INTR_FLAG_LEVELMASK (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3| \ |
||||
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. |
||||
*/ |
||||
#define ESP_PRIO_TO_FLAGS(priority) \ |
||||
((priority) > 0 ? ((1 << (priority)) & ESP_INTR_FLAG_LEVELMASK) : 0) |
||||
|
||||
/*
|
||||
* Check interrupt flags from input and filter unallowed values. |
||||
*/ |
||||
#define ESP_INT_FLAGS_CHECK(int_flags) ((int_flags) & ESP_INTR_FLAG_SHARED) |
||||
|
||||
|
||||
/* Function prototype for interrupt handler function */ |
||||
typedef void (*isr_handler_t)(const void *arg); |
||||
|
||||
/**
|
||||
* @brief Initializes interrupt table to its defaults |
||||
*/ |
||||
void esp_intr_initialize(void); |
||||
|
||||
/**
|
||||
* @brief Allocate an interrupt with the given parameters. |
||||
* |
||||
* This finds an interrupt that matches the restrictions as given in the flags |
||||
* parameter, maps the given interrupt source to it and hooks up the given |
||||
* interrupt handler (with optional argument) as well. If needed, it can return |
||||
* a handle for the interrupt as well. |
||||
* |
||||
* @param source The interrupt source. |
||||
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the |
||||
* choice of interrupts that this routine can choose from. If this value |
||||
* is 0, it will default to allocating a non-shared interrupt of level |
||||
* 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared |
||||
* interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return |
||||
* from this function with the interrupt disabled. |
||||
* @param handler The interrupt handler. |
||||
* @param arg Optional argument for passed to the interrupt handler |
||||
* @param ret_handle Pointer to a struct intr_handle_data_t pointer to store a handle that can |
||||
* later be used to request details or free the interrupt. Can be NULL if no handle |
||||
* is required. |
||||
* |
||||
* @return -EINVAL if the combination of arguments is invalid. |
||||
* -ENODEV No free interrupt found with the specified flags |
||||
* 0 otherwise |
||||
*/ |
||||
int esp_intr_alloc(int source, |
||||
int flags, |
||||
isr_handler_t handler, |
||||
void *arg, |
||||
void **ret_handle); |
||||
|
||||
/**
|
||||
* @brief Disable the interrupt associated with the source |
||||
* |
||||
* @param source The interrupt source |
||||
* |
||||
* @return -EINVAL if the combination of arguments is invalid. |
||||
* 0 otherwise |
||||
*/ |
||||
int esp_intr_disable(int source); |
||||
|
||||
/**
|
||||
* @brief Enable the interrupt associated with the source |
||||
* |
||||
* @param source The interrupt source |
||||
* @return -EINVAL if the combination of arguments is invalid. |
||||
* 0 otherwise |
||||
*/ |
||||
int esp_intr_enable(int source); |
||||
|
||||
/**
|
||||
* @brief Gets the current enabled interrupts |
||||
* |
||||
* @param status_mask_number the status mask can be 0 or 1 |
||||
* @return bitmask of enabled interrupt sources |
||||
*/ |
||||
uint32_t esp_intr_get_enabled_intmask(int status_mask_number); |
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_INTERRUPT_CONTROLLER_INTC_ESP32C3_H_ */ |
Loading…
Reference in new issue