Skip to content

Commit

Permalink
arch: common: refactor multi-level IRQ code
Browse files Browse the repository at this point in the history
Refactor multi-level IRQ related code from `sw_isr_common.c` to
`multilevel_irq.c` to simplify `sw_isr_common` & macrologies.

Signed-off-by: Yong Cong Sin <ycsin@meta.com>
  • Loading branch information
ycsin committed Nov 6, 2023
1 parent 79e6716 commit 07dff11
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 179 deletions.
5 changes: 5 additions & 0 deletions arch/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ if (CONFIG_DYNAMIC_INTERRUPTS AND CONFIG_GEN_ISR_TABLES)
)
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
Expand Down
7 changes: 7 additions & 0 deletions arch/common/include/sw_isr_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
167 changes: 167 additions & 0 deletions arch/common/multilevel_irq.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Copyright (c) 2018 Intel Corporation.
* Copyright (c) 2023 Meta.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/device.h>
#include <zephyr/sw_isr_table.h>
#include <zephyr/sys/util.h>

/*
* 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;
}
181 changes: 2 additions & 179 deletions arch/common/sw_isr_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,191 +4,14 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/device.h>
#include "sw_isr_common.h"
#include <zephyr/sw_isr_table.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/irq.h>
#include <zephyr/irq_multilevel.h>
#include <zephyr/sys/__assert.h>
/*
* 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)
Expand Down

0 comments on commit 07dff11

Please sign in to comment.