zephyr/arch/common/sw_isr_common.c

151 lines
3.7 KiB
C

/*
* Copyright (c) 2018 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/sw_isr_table.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/irq.h>
#include <zephyr/sys/__assert.h>
/*
* Common code for arches that use software ISR tables (CONFIG_GEN_ISR_TABLES)
*/
#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]
= { 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]
= { 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 = 0U;
for (i = 0U; 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 */
unsigned int z_get_sw_isr_table_idx(unsigned int irq)
{
unsigned int table_idx;
#ifdef CONFIG_MULTI_LEVEL_INTERRUPTS
unsigned int level, parent_irq, parent_offset;
level = irq_get_level(irq);
if (level == 2U) {
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 == 3U) {
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 */
return table_idx;
}
void __weak z_isr_install(unsigned int irq, void (*routine)(const void *),
const void *param)
{
unsigned int table_idx;
/*
* Do not assert on the IRQ enable status for ARM GIC since the SGI
* type interrupts are always enabled and attempting to install an ISR
* for them will cause the assertion to fail.
*/
#ifndef CONFIG_GIC
__ASSERT(!irq_is_enabled(irq), "IRQ %d is enabled", irq);
#endif /* !CONFIG_GIC */
table_idx = z_get_sw_isr_table_idx(irq);
/* If dynamic IRQs are enabled, then the _sw_isr_table is in RAM and
* can be modified
*/
_sw_isr_table[table_idx].arg = param;
_sw_isr_table[table_idx].isr = routine;
}
/* Some architectures don't/can't interpret flags or priority and have
* no more processing to do than this. Provide a generic fallback.
*/
int __weak arch_irq_connect_dynamic(unsigned int irq,
unsigned int priority,
void (*routine)(const void *),
const void *parameter,
uint32_t flags)
{
ARG_UNUSED(flags);
ARG_UNUSED(priority);
z_isr_install(irq, routine, parameter);
return irq;
}
#endif /* CONFIG_DYNAMIC_INTERRUPTS */