diff --git a/arch/xtensa/include/esp32/irq.h b/arch/xtensa/include/esp32/irq.h index a3cbab72c1d..6c0373856dd 100644 --- a/arch/xtensa/include/esp32/irq.h +++ b/arch/xtensa/include/esp32/irq.h @@ -390,7 +390,8 @@ #define ESP32_CPUINT_NINTERNAL 6 -#define ESP32_CPUINT_MAX 31 +#define ESP32_NCPUINTS 32 +#define ESP32_CPUINT_MAX (ESP32_NCPUINTS - 1) #define EPS32_CPUINT_PERIPHSET 0xdffe773f #define EPS32_CPUINT_INTERNALSET 0x200188c0 diff --git a/arch/xtensa/src/common/xtensa_int_handlers.S b/arch/xtensa/src/common/xtensa_int_handlers.S index bf83f0cb09d..91c673b07eb 100644 --- a/arch/xtensa/src/common/xtensa_int_handlers.S +++ b/arch/xtensa/src/common/xtensa_int_handlers.S @@ -126,45 +126,25 @@ * a consequence of context switching. */ - mov a12, sp /* a12 = address of save area */ - -._xtensa_dispatch_level&level&: + mov a12, sp /* Address of save area */ +#ifdef __XTENSA_CALL0_ABI__ /* Get mask of pending, enabled interrupts at this level into a2. */ rsr a2, INTENABLE rsr a3, INTERRUPT movi a4, \mask and a2, a2, a3 - and a2, a2, a4 - beqz a2, 5f /* Nothing to do */ - - /* If multiple bits are set then MSB has highest priority. */ - - extract_msb a4, a2 /* a4 = MSB of a2, a2 trashed */ - - /* Check for a timer interrupt. - * - * REVISIT: XT_TIMER_INTEN will be only one of the configured timers - * selected as the system periodic timer (see xtensa_timer.h). There - * is no mechanism here to detect other timer interrupts. - */ - - movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */ - wsr a4, INTCLEAR /* Clear sw or edge-triggered interrupt */ - beq a3, a4, 4f /* If timer interrupt then skip decode */ + and a2, a2, a4 /* a2 = Set of pending, enabled interrupts for this level */ + beqz a2, 3f /* Nothing to do */ /* Call xtensa_int_decode with, passing that address of the register save * area as a parameter (A2). */ -#ifdef __XTENSA_CALL0_ABI__ - mov a2, a12 /* Argument: Top of stack = register save area */ + /* Argument 1: Set of CPU interrupt to dispatch */ + mov a3, sp /* Argument 2: Top of stack = register save area */ call0 xtensa_int_decode /* Call xtensa_int_decode */ -#else - mov a6, a12 /* Argument: Top of stack = register save area */ - call4 xtensa_int_decode /* Call xtensa_int_decode */ -#endif /* On return from xtensa_int_decode, a2 will contain the address of the new * register save area. Usually this would be the same as the current SP. @@ -172,7 +152,7 @@ * register save area. This may or may not reside on a stack. */ - beq a2, a12, 3f /* If timer interrupt then keep stack */ + beq a2, sp, 3f /* If no context switch then keep stack */ /* Switch stacks */ @@ -180,53 +160,42 @@ l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */ addi sp, a2, -(4 * XCPTCONTEXT_SIZE) -3: - j ._xtensa_dispatch_level&level& /* Check for more interrupts */ +#else + /* Get mask of pending, enabled interrupts at this level into a2. */ -4: + rsr a6, INTENABLE + rsr a2, INTERRUPT + movi a3, \mask + and a6, a6, a2 + and a6, a6, a3 /* a6 = Set of pending, enabled interrupts for this level */ + beqz a6, 3f /* Nothing to do */ - .ifeq XT_TIMER_INTPRI - \level - - /* Interrupt handler for the NuttX system timer if at this level. - * We'll be reading the interrupt state again after this call - * so no need to preserve any registers except a7 (pointer to - * state save area). - * - * REVISIT: Here we explicitly assume that the INTERRUPT XT_TIMER_INTEN - * corresponds to TIMER0. That is probably that case, but not necessarily - * so. xtensa_timer.h should probably select the IRQ number as well. + /* Call xtensa_int_decode with, passing that address of the register save + * area as a parameter (A2). */ -#ifdef __XTENSA_CALL0_ABI__ - movi a2, XTENSA_IRQ_TIMER0 /* Argument 1: Timer0 IRQ number */ - mov a3, a12 /* Argument 2: Top of stack = register save area */ - call0 xtensa_irq_dispatch /* Call xtensa_int_decode */ -#else - movi a6, XTENSA_IRQ_TIMER0 /* Argument 1: Timer0 IRQ number */ - mov a7, a12 /* Argument 2: Top of stack = register save area */ - call4 xtensa_irq_dispatch /* Call xtensa_int_decode */ + /* Argument 1: Set of CPU interrupt to dispatch */ + mov a7, sp /* Argument 2: Top of stack = register save area */ + call4 xtensa_int_decode /* Call xtensa_int_decode */ + + /* On return from xtensa_int_decode, a2 will contain the address of the new + * register save area. Usually this would be the same as the current SP. + * But in the event of a context switch, a2 will instead refer to the TCB + * register save area. This may or may not reside on a stack. + */ + + beq a4, sp, 3f /* If no context switch then keep stack */ + + /* Switch stacks */ + + mov a12, a4 /* Switch to the save area of the new thread */ + l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */ + addi sp, a2, -(4 * XCPTCONTEXT_SIZE) #endif - /* On return from xtensa_irq_dispatch, A2 will contain the address of the new - * register save area. Usually this would be the same as the current SP. - * But in the event of a context switch, A2 will instead refer to the TCB - * register save area. - */ - - beq a2, a12, 5f /* If timer interrupt then skip table */ - - /* Switch stacks */ - - mov a12, a2 /* Switch to the save area of the new thread */ - l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */ - addi sp, a2, -(4 * XCPTCONTEXT_SIZE) - .endif - - j ._xtensa_dispatch_level&level& /* Check for more interrupts */ - -5: - /* done */ + /* Done */ +3: .endm /**************************************************************************** diff --git a/arch/xtensa/src/esp32/esp32_cpuint.c b/arch/xtensa/src/esp32/esp32_cpuint.c index 84a1f9abd91..d3cb0ef634d 100644 --- a/arch/xtensa/src/esp32/esp32_cpuint.c +++ b/arch/xtensa/src/esp32/esp32_cpuint.c @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -147,6 +148,17 @@ #define ESP32_MAX_PRIORITY 5 #define ESP32_PRIO_INDEX(p) ((p) - ESP32_MIN_PRIORITY) +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Maps a CPU interrupt to the attached peripheral interrupt */ + +uint8_t g_cpu0_intmap[ESP32_NCPUINTS]; +#ifdef CONFIG_SMP +uint8_t g_cpu1_intmap[ESP32_NCPUINTS]; +#endif + /**************************************************************************** * Private Data ****************************************************************************/ @@ -156,13 +168,9 @@ */ #ifdef CONFIG_SMP - static uint32_t g_intenable[CONFIG_SMP_NCPUS]; - #else - static uint32_t g_intenable[1]; - #endif /* Bitsets for free, unallocated CPU interrupts available to peripheral @@ -170,11 +178,8 @@ static uint32_t g_intenable[1]; */ static uint32_t g_cpu0_freeints = EPS32_CPUINT_PERIPHSET; - #ifdef CONFIG_SMP - static uint32_t g_cpu1_freeints = EPS32_CPUINT_PERIPHSET; - #endif /* Bitsets for each interrupt priority 1-5 */ @@ -311,6 +316,7 @@ int esp32_alloc_cpuint(uint32_t intmask) int esp32_cpuint_initialize(void) { uintptr_t regaddr; + uint8_t *intmap; #ifdef CONFIG_SMP int cpu; #endif @@ -345,6 +351,38 @@ int esp32_cpuint_initialize(void) putreg32(NO_CPUINT, regaddr); } + /* Initialize CPU0-to-peripheral mapping table */ + +#ifdef CONFIG_SMP + if (cpu != 0) + { + intmap = g_cpu1_intmap; + } + else +#endif + { + intmap = g_cpu0_intmap; + } + + /* Indiate that no peripheral interrupts are assigned to CPU interrupts */ + + memset(intmap, CPUINT_UNASSIGNED, ESP32_NCPUINTS); + + /* Special case the 6 internal interrupts. + * + * CPU interrupt bit IRQ number + * --------------------------- --------------------- + * ESP32_CPUINT_TIMER0 6 XTENSA_IRQ_TIMER0 0 + * SP32_CPUINT_SOFTWARE0 7 Not yet defined + * ESP32_CPUINT_PROFILING 11 Not yet defined + * ESP32_CPUINT_TIMER1 15 XTENSA_IRQ_TIMER1 1 + * ESP32_CPUINT_TIMER2 16 XTENSA_IRQ_TIMER2 2 + * ESP32_CPUINT_SOFTWARE1 29 Not yet defined + */ + + intmap[ESP32_CPUINT_TIMER0] = XTENSA_IRQ_TIMER0; + intmap[ESP32_CPUINT_TIMER1] = XTENSA_IRQ_TIMER1; + intmap[ESP32_CPUINT_TIMER2] = XTENSA_IRQ_TIMER2; return OK; } @@ -524,6 +562,7 @@ void esp32_free_cpuint(int cpuint) void esp32_attach_peripheral(int cpu, int periphid, int cpuint) { uintptr_t regaddr; + uint8_t *intmap; DEBUGASSERT(periphid >= 0 && periphid < ESP32_NPERIPHERALS); DEBUGASSERT(cpuint >= 0 && cpuint <= ESP32_CPUINT_MAX); @@ -533,13 +572,18 @@ void esp32_attach_peripheral(int cpu, int periphid, int cpuint) if (cpu != 0) { regaddr = DPORT_APP_MAP_REGADDR(periphid); + intmap = g_cpu1_intmap; } else #endif { regaddr = DPORT_PRO_MAP_REGADDR(periphid); + intmap = g_cpu0_intmap; } + DEBUGASSERT(intmap[cpuint] == CPUINT_UNASSIGNED); + intmap[cpuint] = periphid + XTENSA_IRQ_FIRSTPERIPH; + putreg32(cpuint, regaddr); } @@ -564,6 +608,7 @@ void esp32_attach_peripheral(int cpu, int periphid, int cpuint) void esp32_detach_peripheral(int cpu, int periphid, int cpuint) { uintptr_t regaddr; + uint8_t *intmap; DEBUGASSERT(periphid >= 0 && periphid < ESP32_NPERIPHERALS); #ifdef CONFIG_SMP @@ -572,12 +617,17 @@ void esp32_detach_peripheral(int cpu, int periphid, int cpuint) if (cpu != 0) { regaddr = DPORT_APP_MAP_REGADDR(periphid); + intmap = g_cpu1_intmap; } else #endif { regaddr = DPORT_PRO_MAP_REGADDR(periphid); + intmap = g_cpu0_intmap; } + DEBUGASSERT(intmap[cpuint] != CPUINT_UNASSIGNED); + intmap[cpuint] = CPUINT_UNASSIGNED; + putreg32(NO_CPUINT, regaddr); } diff --git a/arch/xtensa/src/esp32/esp32_cpuint.h b/arch/xtensa/src/esp32/esp32_cpuint.h index eab06bf1973..9d825476aa3 100644 --- a/arch/xtensa/src/esp32/esp32_cpuint.h +++ b/arch/xtensa/src/esp32/esp32_cpuint.h @@ -42,6 +42,25 @@ #include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CPUINT_UNASSIGNED 0xff /* No peripheral assigned to this CPU interrupt */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Maps a CPU interrupt to the attached peripheral interrupt */ + +extern uint8_t g_cpu0_intmap[ESP32_NCPUINTS]; +#ifdef CONFIG_SMP +extern uint8_t g_cpu1_intmap[ESP32_NCPUINTS]; +#endif + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ diff --git a/arch/xtensa/src/esp32/esp32_intdecode.c b/arch/xtensa/src/esp32/esp32_intdecode.c index 675c5327e66..0bc40415246 100644 --- a/arch/xtensa/src/esp32/esp32_intdecode.c +++ b/arch/xtensa/src/esp32/esp32_intdecode.c @@ -39,29 +39,32 @@ #include -#include -#include +#include +#include + +#include +#include -#include "chip/esp32_dport.h" #include "xtensa.h" +#include "esp32_cpuint.h" /**************************************************************************** - * Private Data + * Private Functions ****************************************************************************/ -static const uint8_t g_baseirq[3] = -{ - ESP32_IRQ_SREG0, - ESP32_IRQ_SREG1, - ESP32_IRQ_SREG2 -}; +/**************************************************************************** + * Name: xtensa_intclear + ****************************************************************************/ -static const uint8_t g_nirqs[3] = +static inline void xtensa_intclear(uint32_t mask) { - ESP32_NIRQS_SREG0, - ESP32_NIRQS_SREG1, - ESP32_NIRQS_SREG2 -}; + __asm__ __volatile__ + ( + "movi a2, 0\n" + "wsr %0, INTCLEAR\n" + : "=r"(mask) : : + ); +} /**************************************************************************** * Public Functions @@ -75,80 +78,74 @@ static const uint8_t g_nirqs[3] = * handling to the registered interrupt handler via xtensa_irq_dispatch(). * * Input Parameters: - * regs - Saves processor state on the stack + * cpuints - Set of pending interrupts valid for this level + * regs - Saves processor state on the stack * * Returned Value: - * Normally the same vale as regs is returned. But, in the event of an + * Normally the same value as regs is returned. But, in the event of an * interrupt level context switch, the returned value will, instead point * to the saved processor state in the TCB of the newly started task. * ****************************************************************************/ -uint32_t *xtensa_int_decode(uint32_t *regs) +uint32_t *xtensa_int_decode(uint32_t cpuints, uint32_t *regs) { - uintptr_t regaddr; - uint32_t regval; + uint8_t *intmap; uint32_t mask; - int regndx; int bit; - int baseirq; - int nirqs; - #ifdef CONFIG_SMP int cpu; +#endif - /* Select PRO or APP interrupt status registers */ +#ifdef CONFIG_SMP + /* Select PRO or APP CPU interrupt mapping table */ cpu = up_cpu_index(); - if (cpu == 0) + if (cpu != 0) { - regaddr = DPORT_PRO_INTR_STATUS_0_REG; + intmap = g_cpu1_intmap; } else #endif { - regaddr = DPORT_APP_INTR_STATUS_0_REG; + intmap = g_cpu0_intmap; } - /* Process each pending interrupt in each of the three interrupt status - * registers. - */ + /* Skip over zero bits, eight at a time */ - for (regndx = 0; regndx < 3; regndx++) + for (bit = 0, mask = 0xff; + bit < ESP32_NCPUINTS && (cpuints & mask) == 0; + bit += 8, mask <<= 8); + + /* Process each pending CPU interrupt */ + + for (; bit < ESP32_NCPUINTS && cpuints != 0; bit++) { - /* Fetch the next register status register */ - - regval = getreg32(regaddr); - regaddr += sizeof(uint32_t); - - /* Set up the search */ - - baseirq = g_baseirq[regndx]; - nirqs = g_nirqs[regndx]; - - /* Decode and dispatch each pending bit in the interrupt status - * register. - */ - - for (bit = 0; regval != 0 && bit < nirqs; bit++) + mask = (1 << bit); + if ((cpuints & mask) != 0) { - /* Check if this interrupt is pending */ + /* Extract the IRQ number from the mapping table */ - mask = (1 << bit); - if ((regval & mask) != 0) - { - /* Yes.. Dispatch the interrupt. Note that regs may be - * altered in the case of an interrupt level context switch. - */ + uint8_t irq = intmap[bit]; + DEBUGASSERT(irq != CPUINT_UNASSIGNED); - regs = xtensa_irq_dispatch(baseirq + bit, regs); + /* Clear software or edge-triggered interrupt */ - /* Clear this bit in the sampled status register so that - * perhaps we can exit this loop sooner. - */ + xtensa_intclear(mask); - regval &= ~mask; - } + /* Dispatch the CPU interrupt. + * + * NOTE that regs may be altered in the case of an interrupt + * level context switch. + */ + + regs = xtensa_irq_dispatch((int)irq, regs); + + /* Clear the bit in the pending interrupt so that perhaps + * we can exit the look early. + */ + + cpuints &= ~mask; } }