diff --git a/arch/common/CMakeLists.txt b/arch/common/CMakeLists.txt index 4aa3ba6efccfcba..3590992fdac58cf 100644 --- a/arch/common/CMakeLists.txt +++ b/arch/common/CMakeLists.txt @@ -14,6 +14,11 @@ if (CONFIG_DYNAMIC_INTERRUPTS) ) endif() +zephyr_library_sources_ifdef( + CONFIG_MULTI_LEVEL_INTERRUPTS + multilevel_irq.c + ) + zephyr_library_sources_ifdef(CONFIG_SHARED_INTERRUPTS shared_irq.c) if(NOT CONFIG_ARCH_HAS_TIMING_FUNCTIONS AND diff --git a/arch/common/include/sw_isr_common.h b/arch/common/include/sw_isr_common.h index 223c9ba2442a2f3..05a7d96e4af179b 100644 --- a/arch/common/include/sw_isr_common.h +++ b/arch/common/include/sw_isr_common.h @@ -27,7 +27,14 @@ extern "C" { * * @return corresponding index in _sw_isr_table */ +#ifdef CONFIG_MULTI_LEVEL_INTERRUPTS unsigned int z_get_sw_isr_table_idx(unsigned int irq); +#else +static inline unsigned int z_get_sw_isr_table_idx(unsigned int irq) +{ + return irq - CONFIG_GEN_IRQ_START_VECTOR; +} +#endif /** * @brief Helper function used to get the parent interrupt controller device based on passed IRQ. diff --git a/arch/common/multilevel_irq.c b/arch/common/multilevel_irq.c new file mode 100644 index 000000000000000..96f3ab0801f28d5 --- /dev/null +++ b/arch/common/multilevel_irq.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * Copyright (c) 2023 Meta. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +/* + * Insert code if the node_id is an interrupt controller + */ +#define Z_IF_DT_IS_INTC(node_id, code) \ + IF_ENABLED(DT_NODE_HAS_PROP(node_id, interrupt_controller), (code)) + +/* + * Expands to node_id if its IRQN is equal to `irq`, nothing otherwise + * This only works for `irq` between 0 & 4095, see `IS_EQ` + */ +#define Z_IF_DT_INTC_IRQN_EQ(node_id, irq) IF_ENABLED(IS_EQ(DT_IRQ(node_id, irq), irq), (node_id)) + +/* + * Expands to node_id if it's an interrupt controller & its IRQN is `irq`, or nothing otherwise + */ +#define Z_DT_INTC_GET_IRQN(node_id, irq) \ + Z_IF_DT_IS_INTC(node_id, Z_IF_DT_INTC_IRQN_EQ(node_id, irq)) + +/** + * Loop through child of "/soc" and get root interrupt controllers with `irq` as IRQN, + * this assumes only one device has the IRQN + * @param irq irq number + * @return node_id(s) that has the `irq` number, or empty if none of them has the `irq` + */ +#define INTC_DT_IRQN_GET(irq) \ + DT_FOREACH_CHILD_STATUS_OKAY_VARGS(DT_PATH(soc), Z_DT_INTC_GET_IRQN, irq) + +/* If can't find any matching interrupt controller, fills with `NULL` */ +#define INTC_DEVICE_INIT(node_id) .dev = DEVICE_DT_GET_OR_NULL(node_id), + +#define INIT_IRQ_PARENT_OFFSET(d, i, o) { \ + INTC_DEVICE_INIT(d) \ + .irq = i, \ + .offset = o, \ +} + +#define IRQ_INDEX_TO_OFFSET(i, base) (base + i * CONFIG_MAX_IRQ_PER_AGGREGATOR) + +#define CAT_2ND_LVL_LIST(i, base) \ + INIT_IRQ_PARENT_OFFSET(INTC_DT_IRQN_GET(CONFIG_2ND_LVL_INTR_0##i##_OFFSET), \ + CONFIG_2ND_LVL_INTR_0##i##_OFFSET, IRQ_INDEX_TO_OFFSET(i, base)) +const struct _irq_parent_entry _lvl2_irq_list[CONFIG_NUM_2ND_LEVEL_AGGREGATORS] + = { LISTIFY(CONFIG_NUM_2ND_LEVEL_AGGREGATORS, CAT_2ND_LVL_LIST, (,), + CONFIG_2ND_LVL_ISR_TBL_OFFSET) }; + +#define CAT_3RD_LVL_LIST(i, base) \ + INIT_IRQ_PARENT_OFFSET(INTC_DT_IRQN_GET(CONFIG_3RD_LVL_INTR_0##i##_OFFSET), \ + CONFIG_3RD_LVL_INTR_0##i##_OFFSET, IRQ_INDEX_TO_OFFSET(i, base)) + +#ifdef CONFIG_3RD_LEVEL_INTERRUPTS + +const struct _irq_parent_entry _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 */ + +static const struct _irq_parent_entry *get_parent_entry(unsigned int parent_irq, + const struct _irq_parent_entry list[], + unsigned int length) +{ + unsigned int i; + const struct _irq_parent_entry *entry = NULL; + + for (i = 0U; i < length; ++i) { + if (list[i].irq == parent_irq) { + entry = &list[i]; + break; + } + } + + __ASSERT(i != length, "Invalid argument: %i", parent_irq); + + return entry; +} + +const struct device *z_get_sw_isr_device_from_irq(unsigned int irq) +{ + const struct device *dev = NULL; + unsigned int level, parent_irq; + const struct _irq_parent_entry *entry = NULL; + + level = irq_get_level(irq); + + if (level == 2U) { + parent_irq = irq_parent_level_2(irq); + entry = get_parent_entry(parent_irq, + _lvl2_irq_list, + CONFIG_NUM_2ND_LEVEL_AGGREGATORS); + } +#ifdef CONFIG_3RD_LEVEL_INTERRUPTS + else if (level == 3U) { + parent_irq = irq_parent_level_3(irq); + entry = get_parent_entry(parent_irq, + _lvl3_irq_list, + CONFIG_NUM_3RD_LEVEL_AGGREGATORS); + } +#endif /* CONFIG_3RD_LEVEL_INTERRUPTS */ + dev = entry != NULL ? entry->dev : NULL; + + return dev; +} + +unsigned int z_get_sw_isr_irq_from_device(const struct device *dev) +{ + for (size_t i = 0U; i < CONFIG_NUM_2ND_LEVEL_AGGREGATORS; ++i) { + if (_lvl2_irq_list[i].dev == dev) { + return _lvl2_irq_list[i].irq; + } + } + +#ifdef CONFIG_3RD_LEVEL_INTERRUPTS + for (size_t i = 0U; i < CONFIG_NUM_3RD_LEVEL_AGGREGATORS; ++i) { + if (_lvl3_irq_list[i].dev == dev) { + return _lvl3_irq_list[i].irq; + } + } +#endif /* CONFIG_3RD_LEVEL_INTERRUPTS */ + + return 0; +} + +unsigned int z_get_sw_isr_table_idx(unsigned int irq) +{ + unsigned int table_idx; + unsigned int level, parent_irq, parent_offset; + const struct _irq_parent_entry *entry = NULL; + + level = irq_get_level(irq); + + if (level == 2U) { + parent_irq = irq_parent_level_2(irq); + entry = get_parent_entry(parent_irq, + _lvl2_irq_list, + CONFIG_NUM_2ND_LEVEL_AGGREGATORS); + parent_offset = entry != NULL ? entry->offset : 0U; + 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); + entry = get_parent_entry(parent_irq, + _lvl3_irq_list, + CONFIG_NUM_3RD_LEVEL_AGGREGATORS); + parent_offset = entry != NULL ? entry->offset : 0U; + 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; + + return table_idx; +} diff --git a/arch/common/sw_isr_common.c b/arch/common/sw_isr_common.c index 0e700892210002c..6d69a90ccacea88 100644 --- a/arch/common/sw_isr_common.c +++ b/arch/common/sw_isr_common.c @@ -4,191 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include +#include "sw_isr_common.h" #include -#include #include -#include #include -/* - * Common code for arches that use software ISR tables (CONFIG_GEN_ISR_TABLES) - */ - -#ifdef CONFIG_MULTI_LEVEL_INTERRUPTS - -/* - * Insert code if the node_id is an interrupt controller - */ -#define Z_IF_DT_IS_INTC(node_id, code) \ - IF_ENABLED(DT_NODE_HAS_PROP(node_id, interrupt_controller), (code)) /* - * Expands to node_id if its IRQN is equal to `irq`, nothing otherwise - * This only works for `irq` between 0 & 4095, see `IS_EQ` - */ -#define Z_IF_DT_INTC_IRQN_EQ(node_id, irq) IF_ENABLED(IS_EQ(DT_IRQ(node_id, irq), irq), (node_id)) - -/* - * Expands to node_id if it's an interrupt controller & its IRQN is `irq`, or nothing otherwise - */ -#define Z_DT_INTC_GET_IRQN(node_id, irq) \ - Z_IF_DT_IS_INTC(node_id, Z_IF_DT_INTC_IRQN_EQ(node_id, irq)) - -/** - * Loop through child of "/soc" and get root interrupt controllers with `irq` as IRQN, - * this assumes only one device has the IRQN - * @param irq irq number - * @return node_id(s) that has the `irq` number, or empty if none of them has the `irq` + * Common code for arches that use software ISR tables (CONFIG_GEN_ISR_TABLES) */ -#define INTC_DT_IRQN_GET(irq) \ - DT_FOREACH_CHILD_STATUS_OKAY_VARGS(DT_PATH(soc), Z_DT_INTC_GET_IRQN, irq) - -/* If can't find any matching interrupt controller, fills with `NULL` */ -#define INTC_DEVICE_INIT(node_id) .dev = DEVICE_DT_GET_OR_NULL(node_id), - -#define INIT_IRQ_PARENT_OFFSET(d, i, o) { \ - INTC_DEVICE_INIT(d) \ - .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(INTC_DT_IRQN_GET(CONFIG_2ND_LVL_INTR_0##i##_OFFSET), \ - CONFIG_2ND_LVL_INTR_0##i##_OFFSET, IRQ_INDEX_TO_OFFSET(i, base)) -const struct _irq_parent_entry _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(INTC_DT_IRQN_GET(CONFIG_3RD_LVL_INTR_0##i##_OFFSET), \ - CONFIG_3RD_LVL_INTR_0##i##_OFFSET, IRQ_INDEX_TO_OFFSET(i, base)) -const struct _irq_parent_entry _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 */ - -static const struct _irq_parent_entry *get_parent_entry(unsigned int parent_irq, - const struct _irq_parent_entry list[], - unsigned int length) -{ - unsigned int i; - const struct _irq_parent_entry *entry = NULL; - - for (i = 0U; i < length; ++i) { - if (list[i].irq == parent_irq) { - entry = &list[i]; - break; - } - } - - __ASSERT(i != length, "Invalid argument: %i", parent_irq); - - return entry; -} - -#endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */ - -const struct device *z_get_sw_isr_device_from_irq(unsigned int irq) -{ - const struct device *dev = NULL; - -#ifdef CONFIG_MULTI_LEVEL_INTERRUPTS - unsigned int level, parent_irq; - const struct _irq_parent_entry *entry = NULL; - - level = irq_get_level(irq); - - if (level == 2U) { - parent_irq = irq_parent_level_2(irq); - entry = get_parent_entry(parent_irq, - _lvl2_irq_list, - CONFIG_NUM_2ND_LEVEL_AGGREGATORS); - } -#ifdef CONFIG_3RD_LEVEL_INTERRUPTS - else if (level == 3U) { - parent_irq = irq_parent_level_3(irq); - entry = get_parent_entry(parent_irq, - _lvl3_irq_list, - CONFIG_NUM_3RD_LEVEL_AGGREGATORS); - } -#endif /* CONFIG_3RD_LEVEL_INTERRUPTS */ - dev = entry != NULL ? entry->dev : NULL; -#endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */ - - return dev; -} - -unsigned int z_get_sw_isr_irq_from_device(const struct device *dev) -{ -#ifdef CONFIG_MULTI_LEVEL_INTERRUPTS - for (size_t i = 0U; i < CONFIG_NUM_2ND_LEVEL_AGGREGATORS; ++i) { - if (_lvl2_irq_list[i].dev == dev) { - return _lvl2_irq_list[i].irq; - } - } - -#ifdef CONFIG_3RD_LEVEL_INTERRUPTS - for (size_t i = 0U; i < CONFIG_NUM_3RD_LEVEL_AGGREGATORS; ++i) { - if (_lvl3_irq_list[i].dev == dev) { - return _lvl3_irq_list[i].irq; - } - } -#endif /* CONFIG_3RD_LEVEL_INTERRUPTS */ -#else - ARG_UNUSED(dev); -#endif - - return 0; -} - -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; - const struct _irq_parent_entry *entry = NULL; - - level = irq_get_level(irq); - - if (level == 2U) { - parent_irq = irq_parent_level_2(irq); - entry = get_parent_entry(parent_irq, - _lvl2_irq_list, - CONFIG_NUM_2ND_LEVEL_AGGREGATORS); - parent_offset = entry != NULL ? entry->offset : 0U; - 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); - entry = get_parent_entry(parent_irq, - _lvl3_irq_list, - CONFIG_NUM_3RD_LEVEL_AGGREGATORS); - parent_offset = entry != NULL ? entry->offset : 0U; - 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)