arch: common: refactor multi-level IRQ code
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>
This commit is contained in:
parent
ec93404a26
commit
cdb606aa03
3 changed files with 179 additions and 179 deletions
|
@ -12,6 +12,11 @@ zephyr_library_sources_ifdef(
|
||||||
sw_isr_common.c
|
sw_isr_common.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(
|
||||||
|
CONFIG_MULTI_LEVEL_INTERRUPTS
|
||||||
|
multilevel_irq.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
|
||||||
|
|
167
arch/common/multilevel_irq.c
Normal file
167
arch/common/multilevel_irq.c
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -4,194 +4,22 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <zephyr/device.h>
|
#include "sw_isr_common.h"
|
||||||
#include <zephyr/sw_isr_table.h>
|
#include <zephyr/sw_isr_table.h>
|
||||||
#include <zephyr/arch/cpu.h>
|
|
||||||
#include <zephyr/irq.h>
|
#include <zephyr/irq.h>
|
||||||
#include <zephyr/irq_multilevel.h>
|
|
||||||
#include <zephyr/sys/__assert.h>
|
#include <zephyr/sys/__assert.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Common code for arches that use software ISR tables (CONFIG_GEN_ISR_TABLES)
|
* Common code for arches that use software ISR tables (CONFIG_GEN_ISR_TABLES)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
unsigned int __weak z_get_sw_isr_table_idx(unsigned int irq)
|
||||||
|
{
|
||||||
|
return irq - CONFIG_GEN_IRQ_START_VECTOR;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_DYNAMIC_INTERRUPTS
|
#ifdef CONFIG_DYNAMIC_INTERRUPTS
|
||||||
|
|
||||||
#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`
|
|
||||||
*/
|
|
||||||
#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 *),
|
void __weak z_isr_install(unsigned int irq, void (*routine)(const void *),
|
||||||
const void *param)
|
const void *param)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue