diff --git a/arch/common/sw_isr_common.c b/arch/common/sw_isr_common.c index f1acebc371d..11ce42d41e3 100644 --- a/arch/common/sw_isr_common.c +++ b/arch/common/sw_isr_common.c @@ -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 */ diff --git a/arch/riscv/core/irq_manage.c b/arch/riscv/core/irq_manage.c index b8f4a74057e..c1152dc275e 100644 --- a/arch/riscv/core/irq_manage.c +++ b/arch/riscv/core/irq_manage.c @@ -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 diff --git a/include/irq.h b/include/irq.h index 9a513cc0ac8..6df745d430f 100644 --- a/include/irq.h +++ b/include/irq.h @@ -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 /**