arch: arc: implement DIRECT IRQ support

* implement DIRECT IRQ support both for normal irq and fast irq.
* add separate interrupt stack for fast irq and use CONFIG_ARC_
  _FIRQ_STACK to control it. This will bring shortest interrupt
  latency for fast irq.
* note that scheduing in DIRECT IRQ is not supported.

Signed-off-by: Wayne Ren <wei.ren@synopsys.com>
This commit is contained in:
Wayne Ren 2019-09-25 15:58:14 +08:00 committed by Andrew Boie
commit 601b9afc9e
5 changed files with 215 additions and 0 deletions

View file

@ -95,6 +95,21 @@ config ARC_FIRQ
If FIRQ is disabled, the handle of interrupts with highest priority If FIRQ is disabled, the handle of interrupts with highest priority
will be same with other interrupts. will be same with other interrupts.
config ARC_FIRQ_STACK
bool "Enable separate firq stack"
depends on ARC_FIRQ && RGF_NUM_BANKS > 1
default n
help
Use separate stack for FIRQ handing. When the fast irq is also a direct
irq, this will get the minimal interrupt latency.
config ARC_FIRQ_STACK_SIZE
int "FIRQ stack size"
depends on ARC_FIRQ_STACK
default 1024
help
The size of firq stack.
config ARC_HAS_STACK_CHECKING config ARC_HAS_STACK_CHECKING
bool "ARC has STACK_CHECKING" bool "ARC has STACK_CHECKING"
default y default y

View file

@ -26,6 +26,63 @@
#include <irq.h> #include <irq.h>
#include <sys/printk.h> #include <sys/printk.h>
/*
* storage space for the interrupt stack of fast_irq
*/
#if defined(CONFIG_ARC_FIRQ_STACK)
#if defined(CONFIG_SMP)
K_THREAD_STACK_ARRAY_DEFINE(_firq_interrupt_stack, CONFIG_MP_NUM_CPUS,
CONFIG_ARC_FIRQ_STACK_SIZE);
#else
K_THREAD_STACK_DEFINE(_firq_interrupt_stack, CONFIG_ARC_FIRQ_STACK_SIZE);
#endif
/*
* @brief Set the stack pointer for firq handling
*
* @return N/A
*/
void z_arc_firq_stack_set(void)
{
#ifdef CONFIG_SMP
char *firq_sp = Z_THREAD_STACK_BUFFER(
_firq_interrupt_stack[z_arc_v2_core_id()]) +
CONFIG_ARC_FIRQ_STACK_SIZE;
#else
char *firq_sp = Z_THREAD_STACK_BUFFER(_firq_interrupt_stack) +
CONFIG_ARC_FIRQ_STACK_SIZE;
#endif
/* the z_arc_firq_stack_set must be called when irq diasbled, as
* it can be called not only in the init phase but also other places
*/
unsigned int key = irq_lock();
__asm__ volatile (
/* only ilink will not be banked, so use ilink as channel
* between 2 banks
*/
"mov ilink, %0 \n\t"
"lr %0, [%1] \n\t"
"or %0, %0, %2 \n\t"
"kflag %0 \n\t"
"mov sp, ilink \n\t"
/* switch back to bank0, use ilink to avoid the pollution of
* bank1's gp regs.
*/
"lr ilink, [%1] \n\t"
"and ilink, ilink, %3 \n\t"
"kflag ilink \n\t"
:
: "r"(firq_sp), "i"(_ARC_V2_STATUS32),
"i"(_ARC_V2_STATUS32_RB(1)),
"i"(~_ARC_V2_STATUS32_RB(7))
);
irq_unlock(key);
}
#endif
/* /*
* @brief Enable an interrupt line * @brief Enable an interrupt line
* *
@ -61,6 +118,17 @@ void z_arch_irq_disable(unsigned int irq)
irq_unlock(key); irq_unlock(key);
} }
/**
* @brief Return IRQ enable state
*
* @param irq IRQ line
* @return interrupt enable state, true or false
*/
int z_arch_irq_is_enabled(unsigned int irq)
{
return z_arc_v2_irq_unit_int_enabled(irq);
}
/* /*
* @internal * @internal
* *

View file

@ -143,6 +143,9 @@ _slave_core_wait:
/* get sp set by master core */ /* get sp set by master core */
_get_curr_cpu_irq_stack sp _get_curr_cpu_irq_stack sp
#if defined(CONFIG_ARC_FIRQ_STACK)
jl z_arc_firq_stack_set
#endif
j z_arch_slave_start j z_arch_slave_start
_master_core_startup: _master_core_startup:
@ -167,4 +170,8 @@ _master_core_startup:
mov_s sp, INIT_STACK mov_s sp, INIT_STACK
add sp, sp, INIT_STACK_SIZE add sp, sp, INIT_STACK_SIZE
#if defined(CONFIG_ARC_FIRQ_STACK)
jl z_arc_firq_stack_set
#endif
j @_PrepC j @_PrepC

View file

@ -86,6 +86,22 @@ void z_arc_v2_irq_unit_int_disable(int irq)
z_arc_v2_irq_unit_irq_enable_set(irq, _ARC_V2_INT_DISABLE); z_arc_v2_irq_unit_irq_enable_set(irq, _ARC_V2_INT_DISABLE);
} }
/*
* @brief Poll the enable status of interrupt
*
* Polls the enable status of the specified interrupt
*
* @return 1 enabled, 0 disabled
*/
static ALWAYS_INLINE
bool z_arc_v2_irq_unit_int_enabled(int irq)
{
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_SELECT, irq);
return z_arc_v2_aux_reg_read(_ARC_V2_IRQ_ENABLE) & 0x1;
}
/* /*
* @brief Set interrupt priority * @brief Set interrupt priority
* *

View file

@ -28,10 +28,13 @@ extern "C" {
GTEXT(_irq_exit); GTEXT(_irq_exit);
GTEXT(z_arch_irq_enable) GTEXT(z_arch_irq_enable)
GTEXT(z_arch_irq_disable) GTEXT(z_arch_irq_disable)
GTEXT(z_arc_firq_stack_set)
#else #else
extern void z_arc_firq_stack_set(void);
extern void z_arch_irq_enable(unsigned int irq); extern void z_arch_irq_enable(unsigned int irq);
extern void z_arch_irq_disable(unsigned int irq); extern void z_arch_irq_disable(unsigned int irq);
extern int z_arch_irq_is_enabled(unsigned int irq);
extern void _irq_exit(void); extern void _irq_exit(void);
extern void z_irq_priority_set(unsigned int irq, unsigned int prio, extern void z_irq_priority_set(unsigned int irq, unsigned int prio,
@ -54,6 +57,112 @@ extern void z_irq_spurious(void *unused);
irq_p; \ irq_p; \
}) })
/**
* Configure a 'direct' static interrupt.
*
* When firq has no separate stack(CONFIG_ARC_FIRQ_STACK=N), it's not safe
* to call C ISR handlers because sp will be switched to bank1's sp which
* is undefined value.
* So for this case, the priority cannot be set to 0 but next level 1
*
* When firq has separate stack (CONFIG_ARC_FIRQ_STACK=y) but at the same
* time stack checking is enabled (CONFIG_ARC_STACK_CHECKING=y)
* the stack checking can raise stack check exception as sp is switched to
* firq's stack (bank1's sp). So for this case, the priority cannot be set
* to 0 but next level 1.
*
* Note that for the above cases, if application still wants to use firq by
* setting priority to 0. Application can call z_irq_priority_set again.
* Then it's left to application to handle the details of firq
*
* See include/irq.h for details.
* All arguments must be computable at build time.
*/
#define Z_ARCH_IRQ_DIRECT_CONNECT(irq_p, priority_p, isr_p, flags_p) \
({ \
Z_ISR_DECLARE(irq_p, ISR_FLAG_DIRECT, isr_p, NULL); \
BUILD_ASSERT_MSG(priority_p || !IS_ENABLED(CONFIG_ARC_FIRQ) || \
(IS_ENABLED(CONFIG_ARC_FIRQ_STACK) && \
!IS_ENABLED(CONFIG_ARC_STACK_CHECKING)), \
"irq priority cannot be set to 0 when CONFIG_ARC_FIRQ_STACK" \
"is not configured or CONFIG_ARC_FIRQ_STACK " \
"and CONFIG_ARC_STACK_CHECKING are configured together"); \
z_irq_priority_set(irq_p, priority_p, flags_p); \
irq_p; \
})
static inline void z_arch_isr_direct_header(void)
{
#ifdef CONFIG_TRACING
z_sys_trace_isr_enter();
#endif
}
static inline void z_arch_isr_direct_footer(int maybe_swap)
{
/* clear SW generated interrupt */
if (z_arc_v2_aux_reg_read(_ARC_V2_ICAUSE) ==
z_arc_v2_aux_reg_read(_ARC_V2_AUX_IRQ_HINT)) {
z_arc_v2_aux_reg_write(_ARC_V2_AUX_IRQ_HINT, 0);
}
#ifdef CONFIG_TRACING
z_sys_trace_isr_exit();
#endif
}
#define Z_ARCH_ISR_DIRECT_HEADER() z_arch_isr_direct_header()
extern void z_arch_isr_direct_header(void);
#define Z_ARCH_ISR_DIRECT_FOOTER(swap) z_arch_isr_direct_footer(swap)
/*
* Scheduling can not be done in direct isr. If required, please use kernel
* aware interrupt handling
*/
#define Z_ARCH_ISR_DIRECT_DECLARE(name) \
static inline int name##_body(void); \
__attribute__ ((interrupt("ilink")))void name(void) \
{ \
ISR_DIRECT_HEADER(); \
name##_body(); \
ISR_DIRECT_FOOTER(0); \
} \
static inline int name##_body(void)
/**
*
* @brief Disable all interrupts on the local CPU
*
* This routine disables interrupts. It can be called from either interrupt or
* thread level. This routine returns an architecture-dependent
* lock-out key representing the "interrupt disable state" prior to the call;
* this key can be passed to irq_unlock() to re-enable interrupts.
*
* The lock-out key should only be used as the argument to the
* irq_unlock() API. It should never be used to manually re-enable
* interrupts or to inspect or manipulate the contents of the source register.
*
* This function can be called recursively: it will return a key to return the
* state of interrupt locking to the previous level.
*
* WARNINGS
* Invoking a kernel routine with interrupts locked may result in
* interrupts being re-enabled for an unspecified period of time. If the
* called routine blocks, interrupts will be re-enabled while another
* thread executes, or while the system is idle.
*
* The "interrupt disable state" is an attribute of a thread. Thus, if a
* thread disables interrupts and subsequently invokes a kernel
* routine that causes the calling thread to block, the interrupt
* disable state will be restored when the thread is later rescheduled
* for execution.
*
* @return An architecture-dependent lock-out key representing the
* "interrupt disable state" prior to the call.
*/
static ALWAYS_INLINE unsigned int z_arch_irq_lock(void) static ALWAYS_INLINE unsigned int z_arch_irq_lock(void)
{ {
unsigned int key; unsigned int key;