arch/xtensa: add non-iram interrupt support for ESP32-S2

Adds support for enabling and disabling non-iram interrupts on ESP32-S2.

Signed-off-by: Filipe Cavalcanti <filipe.cavalcanti@espressif.com>
This commit is contained in:
Filipe Cavalcanti
2025-09-30 17:32:17 -03:00
committed by Matteo Golin
parent 02bfd290ac
commit 7297ecc02e
4 changed files with 328 additions and 6 deletions
+10
View File
@@ -39,6 +39,16 @@
#define ESP32S2_INT_PRIO_DEF 1
/* CPU interrupt flags:
* These flags can be used to specify which interrupt qualities the
* code calling esp32s3_setup_irq needs.
*/
#define ESP32S2_CPUINT_FLAG_LEVEL (1 << 0) /* Level-triggered interrupt */
#define ESP32S2_CPUINT_FLAG_EDGE (1 << 1) /* Edge-triggered interrupt */
#define ESP32S2_CPUINT_FLAG_SHARED (1 << 2) /* Interrupt can be shared between ISRs */
#define ESP32S2_CPUINT_FLAG_IRAM (1 << 3) /* ISR can be called if cache is disabled */
/* Interrupt Matrix
*
* The Interrupt Matrix embedded in the ESP32-S2 independently allocates
+219 -2
View File
@@ -35,6 +35,7 @@
#include <nuttx/board.h>
#include <arch/irq.h>
#include <arch/board/board.h>
#include <irq/irq.h>
#include "xtensa.h"
#ifdef CONFIG_ESP32S2_GPIO_IRQ
@@ -101,6 +102,24 @@ static volatile uint8_t g_irqmap[NR_IRQS];
static uint32_t g_intenable;
/* g_non_iram_int_mask is a bitmask of the interrupts that should be
* disabled during a SPI flash operation. Non-IRAM interrupts should always
* be disabled, but interrupts place on IRAM are able to run during a SPI
* flash operation.
*/
static uint32_t g_non_iram_int_mask;
/* g_non_iram_int_disabled[] keeps track of the interrupts disabled during
* a SPI flash operation.
*/
static uint32_t g_non_iram_int_disabled;
/* Flag to indicate that non-IRAM interrupts were disabled */
static bool g_non_iram_int_disabled_flag;
/* Bitsets for free, unallocated CPU interrupts available to peripheral
* devices.
*/
@@ -286,6 +305,9 @@ static void esp32s2_free_cpuint(int cpuint)
void up_irqinitialize(void)
{
int i;
g_non_iram_int_mask = UINT32_MAX;
for (i = 0; i < NR_IRQS; i++)
{
g_irqmap[i] = IRQ_UNMAPPED;
@@ -399,6 +421,21 @@ void up_enable_irq(int irq)
}
else
{
/* Check if the registered ISR for this IRQ is intended to be run from
* IRAM. If so, check if its interrupt handler is located in IRAM.
*/
bool isr_in_iram = !((g_non_iram_int_mask & (1 << cpuint)) > 0);
xcpt_t handler = g_irqvector[irq].handler;
if (isr_in_iram && handler && !esp32s2_ptr_iram(handler))
{
irqerr("Interrupt handler isn't in IRAM (%08" PRIxPTR ")",
(intptr_t)handler);
PANIC();
}
/* For peripheral interrupts, attach the interrupt to the peripheral;
* the CPU interrupt was already enabled when allocated.
*/
@@ -481,7 +518,9 @@ int esp32s2_cpuint_initialize(void)
* periphid - The peripheral number from irq.h to be assigned to
* a CPU interrupt.
* priority - Interrupt's priority (1 - 5).
* type - Interrupt's type (level or edge).
* flags - An ORred mask of the ESP32S3_CPUINT_FLAG_* defines. These
* restrict the choice of interrupts that this routine can
* choose from.
*
* Returned Value:
* The allocated CPU interrupt on success, a negated errno value on
@@ -489,12 +528,22 @@ int esp32s2_cpuint_initialize(void)
*
****************************************************************************/
int esp32s2_setup_irq(int periphid, int priority, int type)
int esp32s2_setup_irq(int periphid, int priority, int flags)
{
irqstate_t irqstate;
uintptr_t regaddr;
int irq;
int cpuint;
int type;
if ((flags & ESP32S2_CPUINT_EDGE) != 0)
{
type = ESP32S2_CPUINT_EDGE;
}
else
{
type = ESP32S2_CPUINT_LEVEL;
}
irqstate = enter_critical_section();
@@ -524,6 +573,15 @@ int esp32s2_setup_irq(int periphid, int priority, int type)
g_irqmap[irq] = cpuint;
regaddr = CORE_MAP_REGADDR(periphid);
if ((flags & ESP32S2_CPUINT_FLAG_IRAM) != 0)
{
esp32s2_irq_set_iram_isr(irq);
}
else
{
esp32s2_irq_unset_iram_isr(irq);
}
putreg32(cpuint, regaddr);
leave_critical_section(irqstate);
@@ -649,3 +707,162 @@ uint32_t *xtensa_int_decode(uint32_t *cpuints, uint32_t *regs)
return regs;
}
/****************************************************************************
* Name: esp32s2_irq_noniram_disable
*
* Description:
* Disable interrupts that aren't specifically marked as running from IRAM
*
* Input Parameters:
* None
*
* Input Parameters:
* None
*
****************************************************************************/
void esp32s2_irq_noniram_disable(void)
{
irqstate_t irqstate;
uint32_t mask;
int bit;
uint32_t oldint;
uint32_t non_iram_ints;
irqstate = enter_critical_section();
non_iram_ints = g_non_iram_int_mask;
ASSERT(!g_non_iram_int_disabled_flag);
g_non_iram_int_disabled_flag = true;
oldint = g_intenable;
for (bit = 0; bit < ESP32S2_NCPUINTS; bit++)
{
mask = 1 << bit;
if ((non_iram_ints & mask) != 0)
{
xtensa_disable_cpuint(&g_intenable, bit);
}
}
g_non_iram_int_disabled = oldint & non_iram_ints;
leave_critical_section(irqstate);
}
/****************************************************************************
* Name: esp32s2_irq_noniram_enable
*
* Description:
* Re-enable interrupts disabled by esp32s2_irq_noniram_disable
*
* Input Parameters:
* None
*
* Input Parameters:
* None
*
****************************************************************************/
void esp32s2_irq_noniram_enable(void)
{
irqstate_t irqstate;
uint32_t mask;
int bit;
uint32_t non_iram_ints;
irqstate = enter_critical_section();
non_iram_ints = g_non_iram_int_disabled;
ASSERT(g_non_iram_int_disabled_flag);
g_non_iram_int_disabled_flag = false;
for (bit = 0; bit < ESP32S2_NCPUINTS; bit++)
{
mask = 1 << bit;
if ((non_iram_ints & mask) != 0)
{
xtensa_enable_cpuint(&g_intenable, bit);
}
}
leave_critical_section(irqstate);
}
/****************************************************************************
* Name: esp32s2_irq_noniram_status
*
* Description:
* Get the current status of non-IRAM interrupts on a specific CPU core
*
* Input Parameters:
* None.
*
* Returned Value:
* True if non-IRAM interrupts are enabled. False otherwise.
*
****************************************************************************/
bool esp32s2_irq_noniram_status()
{
return !g_non_iram_int_disabled_flag;
}
/****************************************************************************
* Name: esp32s3_irq_set_iram_isr
*
* Description:
* Set the ISR associated to an IRQ as a IRAM-enabled ISR.
*
* Input Parameters:
* irq - The associated IRQ to set
*
* Returned Value:
* OK on success; A negated errno value on failure.
*
****************************************************************************/
int esp32s2_irq_set_iram_isr(int irq)
{
int cpuint = CPUINT_GETIRQ(g_cpu_intmap[irq]);
if (cpuint == IRQ_UNMAPPED)
{
return -EINVAL;
}
g_non_iram_int_mask &= ~(1 << cpuint);
return OK;
}
/****************************************************************************
* Name: esp32s2_irq_unset_iram_isr
*
* Description:
* Set the ISR associated to an IRQ as a non-IRAM ISR.
*
* Input Parameters:
* irq - The associated IRQ to set
*
* Returned Value:
* OK on success; A negated errno value on failure.
*
****************************************************************************/
int esp32s2_irq_unset_iram_isr(int irq)
{
int cpuint = CPUINT_GETIRQ(g_cpu_intmap[irq]);
if (cpuint == IRQ_UNMAPPED)
{
return -EINVAL;
}
g_non_iram_int_mask |= (1 << cpuint);
return OK;
}
+86 -4
View File
@@ -46,8 +46,8 @@ extern "C"
/* CPU interrupt types. */
#define ESP32S2_CPUINT_LEVEL 0
#define ESP32S2_CPUINT_EDGE 1
#define ESP32S2_CPUINT_LEVEL ESP32S2_CPUINT_FLAG_LEVEL
#define ESP32S2_CPUINT_EDGE ESP32S2_CPUINT_FLAG_EDGE
/****************************************************************************
* Public Functions Prototypes
@@ -81,7 +81,9 @@ int esp32s2_cpuint_initialize(void);
* periphid - The peripheral number from irq.h to be assigned to
* a CPU interrupt.
* priority - Interrupt's priority (1 - 5).
* type - Interrupt's type (level or edge).
* flags - An ORred mask of the ESP32S3_CPUINT_FLAG_* defines. These
* restrict the choice of interrupts that this routine can
* choose from.
*
* Returned Value:
* The allocated CPU interrupt on success, a negated errno value on
@@ -89,7 +91,7 @@ int esp32s2_cpuint_initialize(void);
*
****************************************************************************/
int esp32s2_setup_irq(int periphid, int priority, int type);
int esp32s2_setup_irq(int periphid, int priority, int flags);
/****************************************************************************
* Name: esp32s2_teardown_irq
@@ -112,6 +114,86 @@ int esp32s2_setup_irq(int periphid, int priority, int type);
void esp32s2_teardown_irq(int periphid, int cpuint);
/****************************************************************************
* Name: esp32s2_irq_noniram_disable
*
* Description:
* Disable interrupts that aren't specifically marked as running from IRAM
*
* Input Parameters:
* None
*
* Input Parameters:
* None
*
****************************************************************************/
void esp32s2_irq_noniram_disable(void);
/****************************************************************************
* Name: esp32s2_irq_noniram_enable
*
* Description:
* Re-enable interrupts disabled by esp32s2_irq_noniram_disable
*
* Input Parameters:
* None
*
* Input Parameters:
* None
*
****************************************************************************/
void esp32s2_irq_noniram_enable(void);
/****************************************************************************
* Name: esp32s2_irq_noniram_status
*
* Description:
* Get the current status of non-IRAM interrupts.
*
* Input Parameters:
* None.
*
* Returned Value:
* True if non-IRAM interrupts are enabled, false otherwise.
*
****************************************************************************/
bool esp32s2_irq_noniram_status(void);
/****************************************************************************
* Name: esp32s2_irq_set_iram_isr
*
* Description:
* Set the ISR associated to an IRQ as a IRAM-enabled ISR.
*
* Input Parameters:
* irq - The associated IRQ to set
*
* Returned Value:
* OK on success; A negated errno value on failure.
*
****************************************************************************/
int esp32s2_irq_set_iram_isr(int irq);
/****************************************************************************
* Name: esp32s2_irq_unset_iram_isr
*
* Description:
* Set the ISR associated to an IRQ as a non-IRAM ISR.
*
* Input Parameters:
* irq - The associated IRQ to set
*
* Returned Value:
* OK on success; A negated errno value on failure.
*
****************************************************************************/
int esp32s2_irq_unset_iram_isr(int irq);
#undef EXTERN
#if defined(__cplusplus)
}
@@ -433,6 +433,19 @@ static inline bool IRAM_ATTR esp32s2_ptr_extram(const void *p)
(intptr_t)p < SOC_EXTRAM_DATA_HIGH);
}
/****************************************************************************
* Name: esp32s2_ptr_iram
*
* Description:
* Check if the pointer is in IRAM
*
****************************************************************************/
static inline bool IRAM_ATTR esp32s2_ptr_iram(const void *p)
{
return ((intptr_t)p >= SOC_IRAM_LOW && (intptr_t)p < SOC_IRAM_HIGH);
}
/****************************************************************************
* Name: esp32s2_ptr_exec
*