arch: isr: Update z_isr_install for multi-level interrupts

z_isr_install is not suited to handle multi-level interrupt formats.
This update allows z_isr_install to accept irq numbers in zephyr format
and place them in the isr table appropriately.

Fixes issue #22145

Signed-off-by: Jaron Kelleher <jkelleher@fb.com>
This commit is contained in:
Jaron Kelleher 2020-03-20 11:24:39 -07:00 committed by Andrew Boie
commit 0fb4382164
3 changed files with 132 additions and 3 deletions

View file

@ -12,9 +12,67 @@
*/
#ifdef CONFIG_DYNAMIC_INTERRUPTS
#ifdef CONFIG_MULTI_LEVEL_INTERRUPTS
struct irq_parent_offset {
unsigned int irq;
unsigned int offset;
};
#define INIT_IRQ_PARENT_OFFSET(i, o) { \
.irq = i, \
.offset = o, \
},
#define IRQ_INDEX_TO_OFFSET(i, base) (base + i * CONFIG_MAX_IRQ_PER_AGGREGATOR)
#ifdef CONFIG_2ND_LEVEL_INTERRUPTS
#define CAT_2ND_LVL_LIST(i, base) \
INIT_IRQ_PARENT_OFFSET(CONFIG_2ND_LVL_INTR_0##i##_OFFSET, \
IRQ_INDEX_TO_OFFSET(i, base))
static struct irq_parent_offset lvl2_irq_list[CONFIG_NUM_2ND_LEVEL_AGGREGATORS]
= { UTIL_LISTIFY(CONFIG_NUM_2ND_LEVEL_AGGREGATORS, CAT_2ND_LVL_LIST,
CONFIG_2ND_LVL_ISR_TBL_OFFSET) };
#endif/* CONFIG_2ND_LEVEL_INTERRUPTS */
#ifdef CONFIG_3RD_LEVEL_INTERRUPTS
#define CAT_3RD_LVL_LIST(i, base) \
INIT_IRQ_PARENT_OFFSET(CONFIG_3RD_LVL_INTR_0##i##_OFFSET, \
IRQ_INDEX_TO_OFFSET(i, base))
static struct irq_parent_offset lvl3_irq_list[CONFIG_NUM_3RD_LEVEL_AGGREGATORS]
= { UTIL_LISTIFY(CONFIG_NUM_3RD_LEVEL_AGGREGATORS, CAT_3RD_LVL_LIST,
CONFIG_3RD_LVL_ISR_TBL_OFFSET) };
#endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
unsigned int get_parent_offset(unsigned int parent_irq,
struct irq_parent_offset list[],
unsigned int length)
{
unsigned int i;
unsigned int offset = 0;
for (i = 0; i < length; ++i) {
if (list[i].irq == parent_irq) {
offset = list[i].offset;
break;
}
}
__ASSERT(i != length, "Invalid argument: %i", parent_irq);
return offset;
}
#endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */
void z_isr_install(unsigned int irq, void (*routine)(void *), void *param)
{
unsigned int table_idx = irq - CONFIG_GEN_IRQ_START_VECTOR;
unsigned int table_idx;
/*
* Do not assert on the IRQ enable status for ARM GIC since the SGI
@ -25,6 +83,38 @@ void z_isr_install(unsigned int irq, void (*routine)(void *), void *param)
__ASSERT(!irq_is_enabled(irq), "IRQ %d is enabled", irq);
#endif /* !CONFIG_GIC */
#ifdef CONFIG_MULTI_LEVEL_INTERRUPTS
unsigned int level;
unsigned int parent_irq;
unsigned int parent_offset;
level = irq_get_level(irq);
if (level == 2) {
parent_irq = irq_parent_level_2(irq);
parent_offset = get_parent_offset(parent_irq,
lvl2_irq_list,
CONFIG_NUM_2ND_LEVEL_AGGREGATORS);
table_idx = parent_offset + irq_from_level_2(irq);
}
#ifdef CONFIG_3RD_LEVEL_INTERRUPTS
else if (level == 3) {
parent_irq = irq_parent_level_3(irq);
parent_offset = get_parent_offset(parent_irq,
lvl3_irq_list,
CONFIG_NUM_3RD_LEVEL_AGGREGATORS);
table_idx = parent_offset + irq_from_level_3(irq);
}
#endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
else {
table_idx = irq;
}
table_idx -= CONFIG_GEN_IRQ_START_VECTOR;
#else
table_idx = irq - CONFIG_GEN_IRQ_START_VECTOR;
#endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */
/* If dynamic IRQs are enabled, then the _sw_isr_table is in RAM and
* can be modified
*/

View file

@ -34,11 +34,20 @@ int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority,
void (*routine)(void *parameter), void *parameter,
u32_t flags)
{
unsigned int level;
ARG_UNUSED(flags);
z_isr_install(irq, routine, parameter);
#if defined(CONFIG_RISCV_HAS_PLIC)
riscv_plic_set_priority(irq, priority);
level = irq_get_level(irq);
if (level == 2) {
irq = irq_from_level_2(irq);
riscv_plic_set_priority(irq, priority);
}
#else
ARG_UNUSED(priority);
#endif

View file

@ -313,6 +313,21 @@ static inline unsigned int irq_to_level_2(unsigned int irq)
{
return (irq + 1) << 8;
}
/**
* @brief Returns the parent IRQ of the level 2 raw IRQ number
* @def irq_parent_level_2()
*
* The parent of a 2nd level interrupt is in the 1st byte
*
* @param irq IRQ number in its zephyr format
*
* @return 2nd level IRQ parent
*/
static inline unsigned int irq_parent_level_2(unsigned int irq)
{
return irq & 0xFF;
}
#endif
#ifdef CONFIG_3RD_LEVEL_INTERRUPTS
@ -342,12 +357,27 @@ static inline unsigned int irq_from_level_3(unsigned int irq)
*
* @param irq IRQ number in its zephyr format
*
* @return 3nd level IRQ number
* @return 3rd level IRQ number
*/
static inline unsigned int irq_to_level_3(unsigned int irq)
{
return (irq + 1) << 16;
}
/**
* @brief Returns the parent IRQ of the level 3 raw IRQ number
* @def irq_parent_level_3()
*
* The parent of a 3rd level interrupt is in the 2nd byte
*
* @param irq IRQ number in its zephyr format
*
* @return 3rd level IRQ parent
*/
static inline unsigned int irq_parent_level_3(unsigned int irq)
{
return (irq >> 8) & 0xFF;
}
#endif
/**