Xtensa ESP32: Basically a redesign of the interrupt dispatch logic.
This commit is contained in:
parent
d4ad5f04d3
commit
cdd8dc72a5
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
@ -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);
|
||||
}
|
||||
|
@ -42,6 +42,25 @@
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <arch/irq.h>
|
||||
|
||||
/****************************************************************************
|
||||
* 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
|
||||
****************************************************************************/
|
||||
|
@ -39,29 +39,32 @@
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
#include <arch/chip/irq.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <arch/irq.h>
|
||||
|
||||
#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:
|
||||
* 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++)
|
||||
{
|
||||
/* Check if this interrupt is pending */
|
||||
|
||||
mask = (1 << bit);
|
||||
if ((regval & mask) != 0)
|
||||
if ((cpuints & mask) != 0)
|
||||
{
|
||||
/* Yes.. Dispatch the interrupt. Note that regs may be
|
||||
* altered in the case of an interrupt level context switch.
|
||||
/* Extract the IRQ number from the mapping table */
|
||||
|
||||
uint8_t irq = intmap[bit];
|
||||
DEBUGASSERT(irq != CPUINT_UNASSIGNED);
|
||||
|
||||
/* Clear software or edge-triggered interrupt */
|
||||
|
||||
xtensa_intclear(mask);
|
||||
|
||||
/* Dispatch the CPU interrupt.
|
||||
*
|
||||
* NOTE that regs may be altered in the case of an interrupt
|
||||
* level context switch.
|
||||
*/
|
||||
|
||||
regs = xtensa_irq_dispatch(baseirq + bit, regs);
|
||||
regs = xtensa_irq_dispatch((int)irq, regs);
|
||||
|
||||
/* Clear this bit in the sampled status register so that
|
||||
* perhaps we can exit this loop sooner.
|
||||
/* Clear the bit in the pending interrupt so that perhaps
|
||||
* we can exit the look early.
|
||||
*/
|
||||
|
||||
regval &= ~mask;
|
||||
}
|
||||
cpuints &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user