1 /*
2  * Copyright (c) 2018 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/sw_isr_table.h>
8 #include <zephyr/arch/cpu.h>
9 #include <zephyr/irq.h>
10 #include <zephyr/sys/__assert.h>
11 /*
12  * Common code for arches that use software ISR tables (CONFIG_GEN_ISR_TABLES)
13  */
14 
15 #ifdef CONFIG_DYNAMIC_INTERRUPTS
16 
17 #ifdef CONFIG_MULTI_LEVEL_INTERRUPTS
18 
19 struct irq_parent_offset {
20 	unsigned int irq;
21 	unsigned int offset;
22 };
23 
24 #define INIT_IRQ_PARENT_OFFSET(i, o) { \
25 	.irq = i, \
26 	.offset = o, \
27 }
28 
29 #define IRQ_INDEX_TO_OFFSET(i, base) (base + i * CONFIG_MAX_IRQ_PER_AGGREGATOR)
30 
31 #ifdef CONFIG_2ND_LEVEL_INTERRUPTS
32 
33 #define CAT_2ND_LVL_LIST(i, base) \
34 	INIT_IRQ_PARENT_OFFSET(CONFIG_2ND_LVL_INTR_0##i##_OFFSET, \
35 		IRQ_INDEX_TO_OFFSET(i, base))
36 static struct irq_parent_offset lvl2_irq_list[CONFIG_NUM_2ND_LEVEL_AGGREGATORS]
37 	= { LISTIFY(CONFIG_NUM_2ND_LEVEL_AGGREGATORS, CAT_2ND_LVL_LIST, (,),
38 		CONFIG_2ND_LVL_ISR_TBL_OFFSET) };
39 
40 #endif/* CONFIG_2ND_LEVEL_INTERRUPTS */
41 
42 #ifdef CONFIG_3RD_LEVEL_INTERRUPTS
43 
44 #define CAT_3RD_LVL_LIST(i, base) \
45 	INIT_IRQ_PARENT_OFFSET(CONFIG_3RD_LVL_INTR_0##i##_OFFSET, \
46 		IRQ_INDEX_TO_OFFSET(i, base))
47 static struct irq_parent_offset lvl3_irq_list[CONFIG_NUM_3RD_LEVEL_AGGREGATORS]
48 	 = { LISTIFY(CONFIG_NUM_3RD_LEVEL_AGGREGATORS, CAT_3RD_LVL_LIST, (,),
49 		CONFIG_3RD_LVL_ISR_TBL_OFFSET) };
50 
51 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
52 
get_parent_offset(unsigned int parent_irq,struct irq_parent_offset list[],unsigned int length)53 unsigned int get_parent_offset(unsigned int parent_irq,
54 					struct irq_parent_offset list[],
55 					unsigned int length)
56 {
57 	unsigned int i;
58 	unsigned int offset = 0U;
59 
60 	for (i = 0U; i < length; ++i) {
61 		if (list[i].irq == parent_irq) {
62 			offset = list[i].offset;
63 			break;
64 		}
65 	}
66 
67 	__ASSERT(i != length, "Invalid argument: %i", parent_irq);
68 
69 	return offset;
70 }
71 
72 #endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */
73 
z_isr_install(unsigned int irq,void (* routine)(const void *),const void * param)74 void z_isr_install(unsigned int irq, void (*routine)(const void *),
75 		   const void *param)
76 {
77 	unsigned int table_idx;
78 
79 	/*
80 	 * Do not assert on the IRQ enable status for ARM GIC since the SGI
81 	 * type interrupts are always enabled and attempting to install an ISR
82 	 * for them will cause the assertion to fail.
83 	 */
84 #ifndef CONFIG_GIC
85 	__ASSERT(!irq_is_enabled(irq), "IRQ %d is enabled", irq);
86 #endif /* !CONFIG_GIC */
87 
88 #ifdef CONFIG_MULTI_LEVEL_INTERRUPTS
89 	unsigned int level;
90 	unsigned int parent_irq;
91 	unsigned int parent_offset;
92 
93 	level = irq_get_level(irq);
94 
95 	if (level == 2U) {
96 		parent_irq = irq_parent_level_2(irq);
97 		parent_offset = get_parent_offset(parent_irq,
98 			lvl2_irq_list,
99 			CONFIG_NUM_2ND_LEVEL_AGGREGATORS);
100 		table_idx = parent_offset + irq_from_level_2(irq);
101 	}
102 #ifdef CONFIG_3RD_LEVEL_INTERRUPTS
103 	else if (level == 3U) {
104 		parent_irq = irq_parent_level_3(irq);
105 		parent_offset = get_parent_offset(parent_irq,
106 			lvl3_irq_list,
107 			CONFIG_NUM_3RD_LEVEL_AGGREGATORS);
108 		table_idx = parent_offset + irq_from_level_3(irq);
109 	}
110 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
111 	else {
112 		table_idx = irq;
113 	}
114 
115 	table_idx -= CONFIG_GEN_IRQ_START_VECTOR;
116 #else
117 	table_idx = irq - CONFIG_GEN_IRQ_START_VECTOR;
118 #endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */
119 
120 	/* If dynamic IRQs are enabled, then the _sw_isr_table is in RAM and
121 	 * can be modified
122 	 */
123 	_sw_isr_table[table_idx].arg = param;
124 	_sw_isr_table[table_idx].isr = routine;
125 }
126 
127 /* Some architectures don't/can't interpret flags or priority and have
128  * no more processing to do than this.  Provide a generic fallback.
129  */
arch_irq_connect_dynamic(unsigned int irq,unsigned int priority,void (* routine)(const void *),const void * parameter,uint32_t flags)130 int __weak arch_irq_connect_dynamic(unsigned int irq,
131 				    unsigned int priority,
132 				    void (*routine)(const void *),
133 				    const void *parameter,
134 				    uint32_t flags)
135 {
136 	ARG_UNUSED(flags);
137 	ARG_UNUSED(priority);
138 
139 	z_isr_install(irq, routine, parameter);
140 	return irq;
141 }
142 
143 #endif /* CONFIG_DYNAMIC_INTERRUPTS */
144