arch: sw_isr: store device info in the table and add funtions to access
Change the internal function to `get_parent_entry`, which returns the entire entry of table. Store the parent interrupt controller device in the `irq_parent_offset` table, and added 2 helper functions to: 1. determine the parent interrupt controller based on the IRQ 2. determine the IRQ of the parent interrupt controller Declare the `struct _irq_parent_entry` in the header and added `-` suffix to the struct so that it can be used to test the functions in testsuites. Signed-off-by: Yong Cong Sin <ycsin@meta.com>
This commit is contained in:
parent
f9a4a3597b
commit
d24545072c
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/sw_isr_table.h>
|
||||
#include <zephyr/arch/cpu.h>
|
||||
#include <zephyr/irq.h>
|
||||
|
@ -17,12 +18,38 @@
|
|||
|
||||
#ifdef CONFIG_MULTI_LEVEL_INTERRUPTS
|
||||
|
||||
struct irq_parent_offset {
|
||||
unsigned int irq;
|
||||
unsigned int offset;
|
||||
};
|
||||
/*
|
||||
* 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))
|
||||
|
||||
#define INIT_IRQ_PARENT_OFFSET(i, o) { \
|
||||
/*
|
||||
* 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, \
|
||||
}
|
||||
|
@ -32,9 +59,9 @@ struct irq_parent_offset {
|
|||
#ifdef CONFIG_2ND_LEVEL_INTERRUPTS
|
||||
|
||||
#define CAT_2ND_LVL_LIST(i, base) \
|
||||
INIT_IRQ_PARENT_OFFSET(CONFIG_2ND_LVL_INTR_0##i##_OFFSET, \
|
||||
IRQ_INDEX_TO_OFFSET(i, base))
|
||||
static struct irq_parent_offset lvl2_irq_list[CONFIG_NUM_2ND_LEVEL_AGGREGATORS]
|
||||
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) };
|
||||
|
||||
|
@ -43,57 +70,113 @@ static struct irq_parent_offset lvl2_irq_list[CONFIG_NUM_2ND_LEVEL_AGGREGATORS]
|
|||
#ifdef CONFIG_3RD_LEVEL_INTERRUPTS
|
||||
|
||||
#define CAT_3RD_LVL_LIST(i, base) \
|
||||
INIT_IRQ_PARENT_OFFSET(CONFIG_3RD_LVL_INTR_0##i##_OFFSET, \
|
||||
IRQ_INDEX_TO_OFFSET(i, base))
|
||||
static struct irq_parent_offset lvl3_irq_list[CONFIG_NUM_3RD_LEVEL_AGGREGATORS]
|
||||
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 */
|
||||
|
||||
unsigned int get_parent_offset(unsigned int parent_irq,
|
||||
struct irq_parent_offset list[],
|
||||
unsigned int length)
|
||||
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;
|
||||
unsigned int offset = 0U;
|
||||
const struct _irq_parent_entry *entry = NULL;
|
||||
|
||||
for (i = 0U; i < length; ++i) {
|
||||
if (list[i].irq == parent_irq) {
|
||||
offset = list[i].offset;
|
||||
entry = &list[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__ASSERT(i != length, "Invalid argument: %i", parent_irq);
|
||||
|
||||
return offset;
|
||||
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);
|
||||
parent_offset = get_parent_offset(parent_irq,
|
||||
lvl2_irq_list,
|
||||
CONFIG_NUM_2ND_LEVEL_AGGREGATORS);
|
||||
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);
|
||||
parent_offset = get_parent_offset(parent_irq,
|
||||
lvl3_irq_list,
|
||||
CONFIG_NUM_3RD_LEVEL_AGGREGATORS);
|
||||
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 */
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#define ZEPHYR_INCLUDE_SW_ISR_TABLE_H_
|
||||
|
||||
#if !defined(_ASMLANGUAGE)
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/toolchain.h>
|
||||
|
||||
|
@ -43,6 +44,12 @@ struct _isr_table_entry {
|
|||
*/
|
||||
extern struct _isr_table_entry _sw_isr_table[];
|
||||
|
||||
struct _irq_parent_entry {
|
||||
const struct device *dev;
|
||||
unsigned int irq;
|
||||
unsigned int 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
|
||||
|
@ -87,6 +94,25 @@ extern struct z_shared_isr_table_entry z_shared_sw_isr_table[];
|
|||
*/
|
||||
unsigned int z_get_sw_isr_table_idx(unsigned int irq);
|
||||
|
||||
/**
|
||||
* @brief Helper function used to get the parent interrupt controller device based on passed IRQ.
|
||||
*
|
||||
* @param irq IRQ number in its zephyr format
|
||||
*
|
||||
* @return corresponding interrupt controller device in _sw_isr_table
|
||||
*/
|
||||
const struct device *z_get_sw_isr_device_from_irq(unsigned int irq);
|
||||
|
||||
/**
|
||||
* @brief Helper function used to get the IRQN of the passed in parent interrupt
|
||||
* controller device.
|
||||
*
|
||||
* @param dev parent interrupt controller device
|
||||
*
|
||||
* @return IRQN of the interrupt controller
|
||||
*/
|
||||
unsigned int z_get_sw_isr_irq_from_device(const struct device *dev);
|
||||
|
||||
/** This interrupt gets put directly in the vector table */
|
||||
#define ISR_FLAG_DIRECT BIT(0)
|
||||
|
||||
|
|
Loading…
Reference in a new issue