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:
parent
a06617b7e1
commit
0fb4382164
3 changed files with 132 additions and 3 deletions
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue