arch: sw_isr: revamp multi-level interrupt architecture

Previously the multi-level irq lookup table is generated by
looping through the devicetree nodes using macros & Kconfig,
which is hard to read and flimsy.

This PR shifts the heavy lifting to devicetree & DT macros such
that an interrupt controller driver, which has its info in the
devicetree, can register itself directly with the multi-level
interrupt architecture, which is more straightforward.

The previous auto-generated look up table with macros is now
moved in a file of its own. A new compatibility Kconfig:
`CONFIG_LEGACY_MULTI_LEVEL_TABLE_GENERATION` is added and
enabled by default to compile the legacy look up table for
interrupt controller drivers that aren't updated to support the
new architecture yet.

Signed-off-by: Yong Cong Sin <ycsin@meta.com>
This commit is contained in:
Yong Cong Sin 2023-12-20 21:06:14 +08:00 committed by Johan Hedberg
commit c5f5b964c1
12 changed files with 307 additions and 129 deletions

View file

@ -22,6 +22,8 @@ zephyr_library_sources_ifdef(
multilevel_irq.c 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) zephyr_library_sources_ifdef(CONFIG_SHARED_INTERRUPTS shared_irq.c)
if(NOT CONFIG_ARCH_HAS_TIMING_FUNCTIONS AND if(NOT CONFIG_ARCH_HAS_TIMING_FUNCTIONS AND

View file

@ -15,3 +15,13 @@ config SEMIHOST
https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc
This option is compatible with hardware and with QEMU, through the This option is compatible with hardware and with QEMU, through the
(automatic) use of the -semihosting-config switch when invoking it. (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.

View file

@ -15,162 +15,71 @@ BUILD_ASSERT((CONFIG_NUM_2ND_LEVEL_AGGREGATORS * CONFIG_MAX_IRQ_PER_AGGREGATOR)
BIT(CONFIG_2ND_LEVEL_INTERRUPT_BITS), BIT(CONFIG_2ND_LEVEL_INTERRUPT_BITS),
"L2 bits not enough to cover the number of L2 IRQs"); "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, * @brief Get the aggregator that's responsible for the given irq
* this assumes only one device has the IRQN *
* @param _irq irq number * @param irq IRQ number to query
* @return node_id(s) that has the `_irq` number, or empty if none of them has the `_irq` *
* @return Aggregator entry, NULL if irq is level 1 or not found.
*/ */
#define INTC_DT_IRQN_GET(_irq) \ static const struct _irq_parent_entry *get_intc_entry_for_irq(unsigned int 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)
{ {
unsigned int i; const unsigned int level = irq_get_level(irq);
const struct _irq_parent_entry *entry = NULL;
for (i = 0U; i < length; ++i) { /* 1st level aggregator is not registered */
if (list[i].irq == parent_irq) { if (level == 1) {
entry = &list[i]; return NULL;
break; }
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 NULL;
return entry;
} }
const struct device *z_get_sw_isr_device_from_irq(unsigned int irq) const struct device *z_get_sw_isr_device_from_irq(unsigned int irq)
{ {
const struct device *dev = NULL; const struct _irq_parent_entry *intc = get_intc_entry_for_irq(irq);
unsigned int level, parent_irq;
const struct _irq_parent_entry *entry = NULL;
level = irq_get_level(irq); __ASSERT(intc != NULL, "can't find an aggregator to handle irq(%X)", irq);
if (level == 2U) { return intc != NULL ? intc->dev : NULL;
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) 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) { /* Get the IRQN for the aggregator */
if (_lvl2_irq_list[i].dev == dev) { STRUCT_SECTION_FOREACH_ALTERNATE(intc_table, _irq_parent_entry, intc) {
return _lvl2_irq_list[i].irq; if (intc->dev == dev) {
return intc->irq;
} }
} }
#ifdef CONFIG_3RD_LEVEL_INTERRUPTS __ASSERT(false, "dev(%p) not found", dev);
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; return 0;
} }
unsigned int z_get_sw_isr_table_idx(unsigned int irq) unsigned int z_get_sw_isr_table_idx(unsigned int irq)
{ {
unsigned int table_idx, level, parent_irq, local_irq, parent_offset; unsigned int table_idx, local_irq;
const struct _irq_parent_entry *entry = NULL; 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) { table_idx = intc->offset + local_irq;
local_irq = irq_from_level_2(irq); } else {
__ASSERT_NO_MSG(local_irq < CONFIG_MAX_IRQ_PER_AGGREGATOR); /* irq level must be 1 if no intc entry */
parent_irq = irq_parent_level_2(irq); __ASSERT(level == 1, "can't find an aggregator to handle irq(%X)", 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 = irq; table_idx = irq;
} }

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2018 Intel Corporation.
* Copyright (c) 2024 Meta.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/sw_isr_table.h>
#include <zephyr/sys/util.h>
/**
* @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 */

View file

@ -205,6 +205,10 @@ if (CONFIG_LOG)
zephyr_iterable_section(NAME log_backend KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN) zephyr_iterable_section(NAME log_backend KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
endif() 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) if (CONFIG_HTTP_SERVER)
zephyr_iterable_section(NAME http_service_desc KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN) zephyr_iterable_section(NAME http_service_desc KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
endif() endif()

View file

@ -20,6 +20,7 @@
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h> #include <zephyr/arch/cpu.h>
#include <zephyr/device.h> #include <zephyr/device.h>
#include <zephyr/devicetree/interrupt_controller.h>
#include <zephyr/shell/shell.h> #include <zephyr/shell/shell.h>
#include <zephyr/sw_isr_table.h> #include <zephyr/sw_isr_table.h>
@ -576,6 +577,10 @@ SHELL_CMD_ARG_REGISTER(plic, &plic_cmds, "PLIC shell commands",
PLIC_INTC_IRQ_FUNC_DEFINE(n) PLIC_INTC_IRQ_FUNC_DEFINE(n)
#define PLIC_INTC_DEVICE_INIT(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_CONFIG_INIT(n) \
PLIC_INTC_DATA_INIT(n) \ PLIC_INTC_DATA_INIT(n) \
DEVICE_DT_INST_DEFINE(n, &plic_init, NULL, \ DEVICE_DT_INST_DEFINE(n, &plic_init, NULL, \

View file

@ -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 <zephyr/devicetree.h>
#include <zephyr/sys/util_macro.h>
/**
* @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_ */

View file

@ -166,6 +166,23 @@ static inline unsigned int irq_parent_level_3(unsigned int irq)
BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS); 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 */ #endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -14,4 +14,6 @@
#include <zephyr/linker/common-rom/common-rom-debug.ld> #include <zephyr/linker/common-rom/common-rom-debug.ld>
#include <zephyr/linker/common-rom/common-rom-interrupt-controllers.ld>
#include <zephyr/linker/common-rom/common-rom-misc.ld> #include <zephyr/linker/common-rom/common-rom-misc.ld>

View file

@ -0,0 +1,9 @@
/*
* Copyright (c) 2024 Meta
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/linker/iterable_sections.h>
ITERABLE_SECTION_ROM(intc_table, 4)

View file

@ -16,6 +16,7 @@
#if !defined(_ASMLANGUAGE) #if !defined(_ASMLANGUAGE)
#include <zephyr/device.h> #include <zephyr/device.h>
#include <zephyr/sys/iterable_sections.h>
#include <zephyr/types.h> #include <zephyr/types.h>
#include <zephyr/toolchain.h> #include <zephyr/toolchain.h>
#include <zephyr/sys/util.h> #include <zephyr/sys/util.h>
@ -47,10 +48,84 @@ extern struct _isr_table_entry _sw_isr_table[];
struct _irq_parent_entry { struct _irq_parent_entry {
const struct device *dev; const struct device *dev;
unsigned int level;
unsigned int irq; unsigned int irq;
unsigned int offset; 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 * Data structure created in a special binary .intlist section for each
* configured interrupt. gen_irq_tables.py pulls this out of the binary and * configured interrupt. gen_irq_tables.py pulls this out of the binary and

View file

@ -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_l3 = irq3 > 0;
const bool has_l2 = irq2 > 0; const bool has_l2 = irq2 > 0;
const uint32_t level = has_l3 ? 3 : has_l2 ? 2 : 1; 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)); 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((hwirq3 + 1) << l3_shift, irq_to_level_3(hwirq3));
zassert_equal(hwirq2 + 1, irq_parent_level_3(irqn)); 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) ZTEST(gen_isr_table, test_multi_level_bit_masks_l1)