diff --git a/arch/common/CMakeLists.txt b/arch/common/CMakeLists.txt index 78fc6396ed6..407d028cffa 100644 --- a/arch/common/CMakeLists.txt +++ b/arch/common/CMakeLists.txt @@ -22,6 +22,8 @@ zephyr_library_sources_ifdef( multilevel_irq.c ) +zephyr_library_sources_ifdef(CONFIG_LEGACY_MULTI_LEVEL_TABLE_GENERATION multilevel_irq_legacy.c) + zephyr_library_sources_ifdef(CONFIG_SHARED_INTERRUPTS shared_irq.c) if(NOT CONFIG_ARCH_HAS_TIMING_FUNCTIONS AND diff --git a/arch/common/Kconfig b/arch/common/Kconfig index aabc599a4d5..d53eb393b27 100644 --- a/arch/common/Kconfig +++ b/arch/common/Kconfig @@ -15,3 +15,13 @@ config SEMIHOST https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc This option is compatible with hardware and with QEMU, through the (automatic) use of the -semihosting-config switch when invoking it. + +config LEGACY_MULTI_LEVEL_TABLE_GENERATION + bool "Auto generates the multi-level interrupt LUT (deprecated)" + default y + select DEPRECATED + depends on MULTI_LEVEL_INTERRUPTS + depends on !PLIC + help + A make-shift Kconfig to continue generating the multi-level interrupt LUT + with the legacy way using DT macros. diff --git a/arch/common/multilevel_irq.c b/arch/common/multilevel_irq.c index 53f8e03a4d8..25972d41da1 100644 --- a/arch/common/multilevel_irq.c +++ b/arch/common/multilevel_irq.c @@ -15,162 +15,71 @@ BUILD_ASSERT((CONFIG_NUM_2ND_LEVEL_AGGREGATORS * CONFIG_MAX_IRQ_PER_AGGREGATOR) BIT(CONFIG_2ND_LEVEL_INTERRUPT_BITS), "L2 bits not enough to cover the number of L2 IRQs"); -/* - * 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` + * @brief Get the aggregator that's responsible for the given irq + * + * @param irq IRQ number to query + * + * @return Aggregator entry, NULL if irq is level 1 or not found. */ -#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) }; - -#ifdef CONFIG_3RD_LEVEL_INTERRUPTS - -BUILD_ASSERT((CONFIG_NUM_3RD_LEVEL_AGGREGATORS * CONFIG_MAX_IRQ_PER_AGGREGATOR) <= - BIT(CONFIG_3RD_LEVEL_INTERRUPT_BITS), - "L3 bits not enough to cover the number of L3 IRQs"); - -#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) +static const struct _irq_parent_entry *get_intc_entry_for_irq(unsigned int irq) { - unsigned int i; - const struct _irq_parent_entry *entry = NULL; + const unsigned int level = irq_get_level(irq); - for (i = 0U; i < length; ++i) { - if (list[i].irq == parent_irq) { - entry = &list[i]; - break; + /* 1st level aggregator is not registered */ + if (level == 1) { + return NULL; + } + + const unsigned int intc_irq = irq_get_intc_irq(irq); + + /* Find an aggregator entry that matches the level & intc_irq */ + STRUCT_SECTION_FOREACH_ALTERNATE(intc_table, _irq_parent_entry, intc) { + if ((intc->level == level) && (intc->irq == intc_irq)) { + return intc; } } - __ASSERT(i != length, "Invalid argument: %i", parent_irq); - - return entry; + return NULL; } 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; + const struct _irq_parent_entry *intc = get_intc_entry_for_irq(irq); - level = irq_get_level(irq); + __ASSERT(intc != NULL, "can't find an aggregator to handle irq(%X)", 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; + return intc != NULL ? intc->dev : NULL; } 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; + /* Get the IRQN for the aggregator */ + STRUCT_SECTION_FOREACH_ALTERNATE(intc_table, _irq_parent_entry, intc) { + if (intc->dev == dev) { + return intc->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 */ + __ASSERT(false, "dev(%p) not found", dev); return 0; } unsigned int z_get_sw_isr_table_idx(unsigned int irq) { - unsigned int table_idx, level, parent_irq, local_irq, parent_offset; - const struct _irq_parent_entry *entry = NULL; + unsigned int table_idx, local_irq; + const struct _irq_parent_entry *intc = get_intc_entry_for_irq(irq); + const unsigned int level = irq_get_level(irq); - level = irq_get_level(irq); + if (intc != NULL) { + local_irq = level == 2 ? irq_from_level_2(irq) : irq_from_level_3(irq); + __ASSERT_NO_MSG(local_irq < CONFIG_MAX_IRQ_PER_AGGREGATOR); - if (level == 2U) { - local_irq = irq_from_level_2(irq); - __ASSERT_NO_MSG(local_irq < CONFIG_MAX_IRQ_PER_AGGREGATOR); - 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 + local_irq; - } -#ifdef CONFIG_3RD_LEVEL_INTERRUPTS - else if (level == 3U) { - local_irq = irq_from_level_3(irq); - __ASSERT_NO_MSG(local_irq < CONFIG_MAX_IRQ_PER_AGGREGATOR); - 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 + local_irq; - } -#endif /* CONFIG_3RD_LEVEL_INTERRUPTS */ - else { + table_idx = intc->offset + local_irq; + } else { + /* irq level must be 1 if no intc entry */ + __ASSERT(level == 1, "can't find an aggregator to handle irq(%X)", irq); table_idx = irq; } diff --git a/arch/common/multilevel_irq_legacy.c b/arch/common/multilevel_irq_legacy.c new file mode 100644 index 00000000000..dd4fe68b5ac --- /dev/null +++ b/arch/common/multilevel_irq_legacy.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * Copyright (c) 2024 Meta. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/** + * @file + * @brief This file houses the deprecated legacy macros-generated multi-level interrupt lookup + * table code, compiled when `CONFIG_LEGACY_MULTI_LEVEL_TABLE_GENERATION` is enabled. + */ + +/* + * 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) + +#define INIT_IRQ_PARENT_OFFSET_2ND(n, d, i, o) \ + IRQ_PARENT_ENTRY_DEFINE(intc_l2_##n, DEVICE_DT_GET_OR_NULL(d), i, o, 2) + +#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_2ND(i, INTC_DT_IRQN_GET(CONFIG_2ND_LVL_INTR_0##i##_OFFSET), \ + CONFIG_2ND_LVL_INTR_0##i##_OFFSET, \ + IRQ_INDEX_TO_OFFSET(i, base)) + +LISTIFY(CONFIG_NUM_2ND_LEVEL_AGGREGATORS, CAT_2ND_LVL_LIST, (;), CONFIG_2ND_LVL_ISR_TBL_OFFSET); + +#ifdef CONFIG_3RD_LEVEL_INTERRUPTS + +BUILD_ASSERT((CONFIG_NUM_3RD_LEVEL_AGGREGATORS * CONFIG_MAX_IRQ_PER_AGGREGATOR) <= + BIT(CONFIG_3RD_LEVEL_INTERRUPT_BITS), + "L3 bits not enough to cover the number of L3 IRQs"); + +#define INIT_IRQ_PARENT_OFFSET_3RD(n, d, i, o) \ + IRQ_PARENT_ENTRY_DEFINE(intc_l3_##n, DEVICE_DT_GET_OR_NULL(d), i, o, 3) + +#define CAT_3RD_LVL_LIST(i, base) \ + INIT_IRQ_PARENT_OFFSET_3RD(i, INTC_DT_IRQN_GET(CONFIG_3RD_LVL_INTR_0##i##_OFFSET), \ + CONFIG_3RD_LVL_INTR_0##i##_OFFSET, \ + IRQ_INDEX_TO_OFFSET(i, base)) + +LISTIFY(CONFIG_NUM_3RD_LEVEL_AGGREGATORS, CAT_3RD_LVL_LIST, (;), CONFIG_3RD_LVL_ISR_TBL_OFFSET); + +#endif /* CONFIG_3RD_LEVEL_INTERRUPTS */ diff --git a/cmake/linker_script/common/common-rom.cmake b/cmake/linker_script/common/common-rom.cmake index 125d438b58b..a902a24f3c0 100644 --- a/cmake/linker_script/common/common-rom.cmake +++ b/cmake/linker_script/common/common-rom.cmake @@ -205,6 +205,10 @@ if (CONFIG_LOG) zephyr_iterable_section(NAME log_backend KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN) endif() +if (CONFIG_MULTI_LEVEL_INTERRUPTS) + zephyr_iterable_section(NAME intc_table KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4) +endif() + if (CONFIG_HTTP_SERVER) zephyr_iterable_section(NAME http_service_desc KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN) endif() diff --git a/drivers/interrupt_controller/intc_plic.c b/drivers/interrupt_controller/intc_plic.c index f5f762e4aba..6f959b25cd4 100644 --- a/drivers/interrupt_controller/intc_plic.c +++ b/drivers/interrupt_controller/intc_plic.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -576,6 +577,10 @@ SHELL_CMD_ARG_REGISTER(plic, &plic_cmds, "PLIC shell commands", PLIC_INTC_IRQ_FUNC_DEFINE(n) #define PLIC_INTC_DEVICE_INIT(n) \ + IRQ_PARENT_ENTRY_DEFINE( \ + plic##n, DEVICE_DT_INST_GET(n), DT_INST_IRQN(n), \ + INTC_INST_ISR_TBL_OFFSET(n), \ + DT_INST_INTC_GET_AGGREGATOR_LEVEL(n)); \ PLIC_INTC_CONFIG_INIT(n) \ PLIC_INTC_DATA_INIT(n) \ DEVICE_DT_INST_DEFINE(n, &plic_init, NULL, \ diff --git a/include/zephyr/devicetree/interrupt_controller.h b/include/zephyr/devicetree/interrupt_controller.h new file mode 100644 index 00000000000..a9c4e864ae1 --- /dev/null +++ b/include/zephyr/devicetree/interrupt_controller.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Meta + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Interrupt controller devicetree macro public API header file. + */ + +#ifndef ZEPHYR_INCLUDE_DEVICETREE_INTERRUPT_CONTROLLER_H_ +#define ZEPHYR_INCLUDE_DEVICETREE_INTERRUPT_CONTROLLER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * @defgroup devicetree-interrupt_controller Devicetree Interrupt Controller API + * @ingroup devicetree + * @{ + */ + +/** + * @brief Get the aggregator level of an interrupt controller + * + * @note Aggregator level is equivalent to IRQ_LEVEL + 1 (a 2nd level aggregator has Zephyr level 1 + * IRQ encoding) + * + * @param node_id node identifier of an interrupt controller + * + * @return Level of the interrupt controller + */ +#define DT_INTC_GET_AGGREGATOR_LEVEL(node_id) UTIL_INC(DT_IRQ_LEVEL(node_id)) + +/** + * @brief Get the aggregator level of a `DT_DRV_COMPAT` interrupt controller + * + * @note Aggregator level is equivalent to IRQ_LEVEL + 1 (a 2nd level aggregator has Zephyr level 1 + * IRQ encoding) + * + * @param inst instance of an interrupt controller + * + * @return Level of the interrupt controller + */ +#define DT_INST_INTC_GET_AGGREGATOR_LEVEL(inst) DT_INTC_GET_AGGREGATOR_LEVEL(DT_DRV_INST(inst)) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DEVICETREE_INTERRUPT_CONTROLLER_H_ */ diff --git a/include/zephyr/irq_multilevel.h b/include/zephyr/irq_multilevel.h index 25daa030249..dda2ff2b46b 100644 --- a/include/zephyr/irq_multilevel.h +++ b/include/zephyr/irq_multilevel.h @@ -166,6 +166,23 @@ static inline unsigned int irq_parent_level_3(unsigned int irq) BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS); } +/** + * @brief Returns the parent interrupt controller IRQ of the given IRQ number + * + * @param irq IRQ number in its zephyr format + * + * @return IRQ of the interrupt controller + */ +static inline unsigned int irq_get_intc_irq(unsigned int irq) +{ + const unsigned int level = irq_get_level(irq); + + __ASSERT_NO_MSG(level > 1 && level <= 3); + + return irq & BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS + + (level == 3 ? CONFIG_2ND_LEVEL_INTERRUPT_BITS : 0)); +} + #endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */ #ifdef __cplusplus } diff --git a/include/zephyr/linker/common-rom.ld b/include/zephyr/linker/common-rom.ld index 8f9c89ebdd0..7653c1843d1 100644 --- a/include/zephyr/linker/common-rom.ld +++ b/include/zephyr/linker/common-rom.ld @@ -14,4 +14,6 @@ #include +#include + #include diff --git a/include/zephyr/linker/common-rom/common-rom-interrupt-controllers.ld b/include/zephyr/linker/common-rom/common-rom-interrupt-controllers.ld new file mode 100644 index 00000000000..9ee9dace341 --- /dev/null +++ b/include/zephyr/linker/common-rom/common-rom-interrupt-controllers.ld @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2024 Meta + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + + ITERABLE_SECTION_ROM(intc_table, 4) diff --git a/include/zephyr/sw_isr_table.h b/include/zephyr/sw_isr_table.h index 7b1bfddb2cb..ea708cba4f7 100644 --- a/include/zephyr/sw_isr_table.h +++ b/include/zephyr/sw_isr_table.h @@ -16,6 +16,7 @@ #if !defined(_ASMLANGUAGE) #include +#include #include #include #include @@ -47,10 +48,84 @@ extern struct _isr_table_entry _sw_isr_table[]; struct _irq_parent_entry { const struct device *dev; + unsigned int level; unsigned int irq; unsigned int offset; }; +/** + * @cond INTERNAL_HIDDEN + */ + +/* Mapping between aggregator level to order */ +#define Z_STR_L2 2ND +#define Z_STR_L3 3RD +/** + * @brief Get the Software ISR table offset Kconfig for the given aggregator level + * + * @param l Aggregator level, must be 2 or 3 + * + * @return `CONFIG_2ND_LVL_ISR_TBL_OFFSET` if second level aggregator, + * `CONFIG_3RD_LVL_ISR_TBL_OFFSET` if third level aggregator + */ +#define Z_SW_ISR_TBL_KCONFIG_BY_ALVL(l) CONCAT(CONFIG_, CONCAT(Z_STR_L, l), _LVL_ISR_TBL_OFFSET) + +/** + * INTERNAL_HIDDEN @endcond + */ + +/** + * @brief Get an interrupt controller node's level base ISR table offset. + * + * @param node_id node identifier of the interrupt controller + * + * @return `CONFIG_2ND_LVL_ISR_TBL_OFFSET` if node_id is a second level aggregator, + * `CONFIG_3RD_LVL_ISR_TBL_OFFSET` if it is a third level aggregator + */ +#define INTC_BASE_ISR_TBL_OFFSET(node_id) \ + Z_SW_ISR_TBL_KCONFIG_BY_ALVL(DT_INTC_GET_AGGREGATOR_LEVEL(node_id)) + +/** + * @brief Get the SW ISR table offset for an instance of interrupt controller + * + * @param inst DT_DRV_COMPAT interrupt controller driver instance number + * + * @return Software ISR table offset of the interrupt controller + */ +#define INTC_INST_ISR_TBL_OFFSET(inst) \ + (INTC_BASE_ISR_TBL_OFFSET(DT_DRV_INST(inst)) + (inst * CONFIG_MAX_IRQ_PER_AGGREGATOR)) + +/** + * @brief Get the SW ISR table offset for a child interrupt controller + * + * @details This macro is a alternative form of the `INTC_INST_ISR_TBL_OFFSET`. This is used by + * pseudo interrupt controller devices that are child of a main interrupt controller device. + * + * @param node_id node identifier of the child interrupt controller + * + * @return Software ISR table offset of the child + */ +#define INTC_CHILD_ISR_TBL_OFFSET(node_id) \ + (INTC_BASE_ISR_TBL_OFFSET(node_id) + \ + (DT_NODE_CHILD_IDX(node_id) * CONFIG_MAX_IRQ_PER_AGGREGATOR)) + +/** + * @brief Register an interrupt controller with the software ISR table + * + * @param _name Name of the interrupt controller (must be unique) + * @param _dev Pointer to the interrupt controller device instance + * @param _irq Interrupt controller IRQ number + * @param _offset Software ISR table offset of the interrupt controller + * @param _level Interrupt controller aggregator level + */ +#define IRQ_PARENT_ENTRY_DEFINE(_name, _dev, _irq, _offset, _level) \ + static const STRUCT_SECTION_ITERABLE_ALTERNATE(intc_table, _irq_parent_entry, _name) = { \ + .dev = _dev, \ + .level = _level, \ + .irq = _irq, \ + .offset = _offset, \ + } + /* * Data structure created in a special binary .intlist section for each * configured interrupt. gen_irq_tables.py pulls this out of the binary and diff --git a/tests/kernel/gen_isr_table/src/main.c b/tests/kernel/gen_isr_table/src/main.c index 2f6476a1f34..95a8940d25c 100644 --- a/tests/kernel/gen_isr_table/src/main.c +++ b/tests/kernel/gen_isr_table/src/main.c @@ -407,7 +407,9 @@ static void test_multi_level_bit_masks_fn(uint32_t irq1, uint32_t irq2, uint32_t const bool has_l3 = irq3 > 0; const bool has_l2 = irq2 > 0; const uint32_t level = has_l3 ? 3 : has_l2 ? 2 : 1; - const uint32_t irqn = (irq3 << l3_shift) | (irq2 << l2_shift) | irq1; + const uint32_t irqn_l1 = irq1; + const uint32_t irqn_l2 = (irq2 << l2_shift) | irqn_l1; + const uint32_t irqn = (irq3 << l3_shift) | irqn_l2; zassert_equal(level, irq_get_level(irqn)); @@ -422,6 +424,17 @@ static void test_multi_level_bit_masks_fn(uint32_t irq1, uint32_t irq2, uint32_t zassert_equal((hwirq3 + 1) << l3_shift, irq_to_level_3(hwirq3)); zassert_equal(hwirq2 + 1, irq_parent_level_3(irqn)); } + + if (has_l3) { + zassert_equal(irqn_l2, irq_get_intc_irq(irqn)); + } else if (has_l2) { + zassert_equal(irqn_l1, irq_get_intc_irq(irqn)); + } else { + /* degenerate cases */ + if (false) { + zassert_equal(irqn, irq_get_intc_irq(irqn)); + } + } } ZTEST(gen_isr_table, test_multi_level_bit_masks_l1)