Skip to content

Commit ff855fa

Browse files
committed
driver: interrupt_controller: intc_xlnx: add Xilinx driver
Add driver for Xilinx interrupt controller. Controller can be used in connection to Microblaze, Microblaze V or Xilinx ARM SOCs. In Microblaze case it is primary interrupt controller. In other cases it is secondary interrupt controller in cascade mode. Patch is adding support for configuration for Microblaze V that's why xilinx_intc_init() is checking if RISCV_IRQ_MEXT is present and creating connection. And because it is secondary interrupt controller CONFIG_2ND_LVL_ISR_TBL_OFFSET is passed via argument. Co-developed-by: Alp Sayin <[email protected]> Signed-off-by: Alp Sayin <[email protected]> Signed-off-by: Michal Simek <[email protected]> Link: zephyrproject-rtos/zephyr#80069 State: waiting
1 parent 4aec012 commit ff855fa

File tree

6 files changed

+335
-0
lines changed

6 files changed

+335
-0
lines changed

drivers/interrupt_controller/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ zephyr_library_sources_ifdef(CONFIG_NXP_PINT intc_nxp_pint.c)
4242
zephyr_library_sources_ifdef(CONFIG_RENESAS_RA_ICU intc_renesas_ra_icu.c)
4343
zephyr_library_sources_ifdef(CONFIG_NXP_IRQSTEER intc_nxp_irqsteer.c)
4444
zephyr_library_sources_ifdef(CONFIG_INTC_MTK_ADSP intc_mtk_adsp.c)
45+
zephyr_library_sources_ifdef(CONFIG_XLNX_INTC intc_xlnx.c)
4546

4647
if(CONFIG_INTEL_VTD_ICTL)
4748
zephyr_library_include_directories(${ZEPHYR_BASE}/arch/x86/include)

drivers/interrupt_controller/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,6 @@ source "drivers/interrupt_controller/Kconfig.nxp_irqsteer"
108108

109109
source "drivers/interrupt_controller/Kconfig.mtk_adsp"
110110

111+
source "drivers/interrupt_controller/Kconfig.xlnx"
112+
111113
endmenu
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright (c) 2023-2024 Advanced Micro Devices, Inc. (AMD)
2+
# Copyright (c) 2023 Alp Sayin <[email protected]>
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
config XLNX_INTC
6+
bool "Xilinx interrupt controller"
7+
help
8+
The AXI Interrupt Controller (INTC).
9+
10+
menu "AXI Interrupt Controller Optional Registers"
11+
depends on XLNX_INTC
12+
13+
config XLNX_INTC_USE_IPR
14+
bool "Use Interrupt Pending Register"
15+
help
16+
Each bit in this register is the logical AND of the bits in the ISR and the IER.
17+
This is an optional read-only register. Don't choose to use it if it doesn't exist.
18+
19+
config XLNX_INTC_USE_SIE
20+
bool "Use Set Interrupt Enables Register"
21+
help
22+
Writing a 1 to a bit location in SIE sets the corresponding bit in the IER.
23+
This is an optional write-only register. Don't choose to use it if it doesn't exist.
24+
25+
config XLNX_INTC_USE_CIE
26+
bool "Use Clear Interrupt Enables Register"
27+
help
28+
Writing a 1 to a bit location in CIE clears the corresponding bit in the IER.
29+
This is an optional write-only register. Don't choose to use it if it doesn't exist.
30+
31+
config XLNX_INTC_USE_IVR
32+
bool "Use Interrupt Vector Register"
33+
help
34+
The IVR contains the ordinal value of the highest priority, enabled, and active interrupt input.
35+
The IVR acts as an index to the correct Interrupt Vector Address.
36+
This is an optional read-only register. Don't choose to use it if it doesn't exist.
37+
38+
config XLNX_INTC_INITIALIZE_IVAR_REGISTERS
39+
bool "Initialize Interrupt Vector Address Registers"
40+
help
41+
The IVAR contains the addresses for fast-interrupts. This flag enables initializing all
42+
the address registers to point them to default interrupt handler which is `0x10` for Microblaze.
43+
44+
endmenu
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
/*
2+
* Copyright (c) 2023 - 2024 Advanced Micro Devices, Inc. (AMD)
3+
* Copyright (c) 2023 Alp Sayin <[email protected]>
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
/*
9+
* This file implements AXI Interrupt Controller (INTC)
10+
* For more details about the INTC see PG 099
11+
*
12+
* The functionality has been based on intc_v3_12 package
13+
*
14+
* Right now the implementation:
15+
* - does not support fast interrupt mode
16+
* - does not support Cascade mode
17+
* - does not support XIN_SVC_SGL_ISR_OPTION
18+
*/
19+
20+
#include <errno.h>
21+
#include <zephyr/arch/cpu.h>
22+
#include <zephyr/devicetree.h>
23+
#include <zephyr/drivers/interrupt_controller/intc_xlnx.h>
24+
#include <zephyr/init.h>
25+
#include <zephyr/kernel.h>
26+
#include <zephyr/logging/log.h>
27+
#include <zephyr/sys/__assert.h>
28+
#include <zephyr/sys/atomic.h>
29+
#include <zephyr/sys_clock.h>
30+
31+
LOG_MODULE_REGISTER(xlnx_intc);
32+
33+
#define DT_DRV_COMPAT xlnx_xps_intc_1_00_a
34+
35+
#define BASE_ADDRESS DT_INST_REG_ADDR(0)
36+
#define INTC_REG(offset) (uint32_t *)(BASE_ADDRESS + offset)
37+
38+
#define xlnx_intc_read(offset) sys_read32(BASE_ADDRESS + offset)
39+
#define xlnx_intc_write(data, offset) sys_write32(data, BASE_ADDRESS + offset)
40+
41+
#define XIN_SVC_SGL_ISR_OPTION 1UL
42+
#define XIN_SVC_ALL_ISRS_OPTION 2UL
43+
44+
#define XIN_ISR_OFFSET 0x0 /* Interrupt Status Register */
45+
#define XIN_IPR_OFFSET 0x4 /* Interrupt Pending Register */
46+
#define XIN_IER_OFFSET 0x8 /* Interrupt Enable Register */
47+
#define XIN_IAR_OFFSET 0xc /* Interrupt Acknowledge Register */
48+
#define XIN_SIE_OFFSET 0x10 /* Set Interrupt Enable Register */
49+
#define XIN_CIE_OFFSET 0x14 /* Clear Interrupt Enable Register */
50+
#define XIN_IVR_OFFSET 0x18 /* Interrupt Vector Register */
51+
#define XIN_MER_OFFSET 0x1C /* Master Enable Register */
52+
#define XIN_IMR_OFFSET 0x20 /* Interrupt Mode Register, only for Fast Interrupt */
53+
#define XIN_IVAR_OFFSET 0x100 /* Interrupt Vector Address Register, only for Fast Interrupt */
54+
55+
/* Bit definitions for the bits of the MER register */
56+
#define XIN_INT_MASTER_ENABLE_MASK BIT(0)
57+
#define XIN_INT_HARDWARE_ENABLE_MASK BIT(1) /* once set cannot be cleared */
58+
59+
struct xlnx_intc_state {
60+
bool is_ready; /* Device is initialized and ready */
61+
bool is_started; /* Device has been started */
62+
};
63+
64+
static struct xlnx_intc_state intc_state = {
65+
.is_ready = false,
66+
.is_started = false,
67+
};
68+
69+
uint32_t xlnx_intc_irq_get_enabled(void)
70+
{
71+
return xlnx_intc_read(XIN_IER_OFFSET);
72+
}
73+
74+
uint32_t xlnx_intc_get_status_register(void)
75+
{
76+
return xlnx_intc_read(XIN_ISR_OFFSET);
77+
}
78+
79+
uint32_t xlnx_intc_irq_pending(void)
80+
{
81+
#if defined(CONFIG_XLNX_INTC_USE_IPR)
82+
return xlnx_intc_read(XIN_IPR_OFFSET);
83+
#else
84+
uint32_t enabled = xlnx_intc_irq_get_enabled();
85+
uint32_t interrupt_status_register = xlnx_intc_get_status_register();
86+
87+
return enabled & interrupt_status_register;
88+
#endif
89+
}
90+
91+
uint32_t xlnx_intc_irq_pending_vector(void)
92+
{
93+
#if defined(CONFIG_XLNX_INTC_USE_IVR)
94+
return xlnx_intc_read(XIN_IVR_OFFSET);
95+
#else
96+
return find_lsb_set(xlnx_intc_irq_pending()) - 1;
97+
#endif
98+
}
99+
100+
void xlnx_intc_irq_enable(uint32_t irq)
101+
{
102+
__ASSERT_NO_MSG(irq < 32);
103+
104+
if (intc_state.is_ready != true) {
105+
LOG_DBG("Interrupt controller is not ready\n");
106+
k_panic();
107+
}
108+
109+
uint32_t mask = BIT(irq);
110+
111+
#if defined(CONFIG_XLNX_INTC_USE_SIE)
112+
xlnx_intc_write(mask, XIN_SIE_OFFSET);
113+
#else
114+
atomic_or((atomic_t *)INTC_REG(XIN_IER_OFFSET), mask);
115+
#endif /* CONFIG_XLNX_INTC_USE_SIE */
116+
}
117+
118+
void xlnx_intc_irq_disable(uint32_t irq)
119+
{
120+
__ASSERT_NO_MSG(irq < 32);
121+
122+
uint32_t mask = BIT(irq);
123+
124+
#if defined(CONFIG_XLNX_INTC_USE_CIE)
125+
xlnx_intc_write(mask, XIN_CIE_OFFSET);
126+
#else
127+
atomic_and((atomic_t *)INTC_REG(XIN_IER_OFFSET), ~mask);
128+
#endif /* CONFIG_XLNX_INTC_USE_CIE */
129+
}
130+
131+
void xlnx_intc_irq_acknowledge(uint32_t mask)
132+
{
133+
xlnx_intc_write(mask, XIN_IAR_OFFSET);
134+
}
135+
136+
int32_t xlnx_intc_controller_init(void)
137+
{
138+
if (intc_state.is_started == true) {
139+
return -EEXIST;
140+
}
141+
142+
/*
143+
* Disable IRQ output signal
144+
* Disable all interrupt sources
145+
* Acknowledge all sources
146+
* Disable fast interrupt mode
147+
*/
148+
xlnx_intc_write(0, XIN_MER_OFFSET);
149+
xlnx_intc_write(0, XIN_IER_OFFSET);
150+
xlnx_intc_write(0xFFFFFFFF, XIN_IAR_OFFSET);
151+
152+
#if defined(CONFIG_XLNX_INTC_INITIALIZE_IVAR_REGISTERS)
153+
xlnx_intc_write(0, XIN_IMR_OFFSET);
154+
155+
for (int idx = 0; idx < 32; idx++) {
156+
xlnx_intc_write(0x10, XIN_IVAR_OFFSET + (idx * 4));
157+
}
158+
#endif
159+
160+
intc_state.is_ready = true;
161+
162+
return 0;
163+
}
164+
165+
int32_t xlnx_intc_irq_start(void)
166+
{
167+
if (intc_state.is_started != false) {
168+
return -EEXIST;
169+
}
170+
if (intc_state.is_ready != true) {
171+
return -ENOENT;
172+
}
173+
174+
intc_state.is_started = true;
175+
176+
xlnx_intc_write(XIN_INT_MASTER_ENABLE_MASK | XIN_INT_HARDWARE_ENABLE_MASK, XIN_MER_OFFSET);
177+
178+
return 0;
179+
}
180+
181+
static void xlnx_irq_handler(const void *arg)
182+
{
183+
uint32_t irq, irq_mask;
184+
struct _isr_table_entry *ite;
185+
uint32_t level = POINTER_TO_UINT(arg);
186+
187+
/* Get the IRQ number generating the interrupt */
188+
irq_mask = xlnx_intc_irq_pending();
189+
irq = find_lsb_set(irq_mask);
190+
191+
/*
192+
* If the IRQ is out of range, call z_irq_spurious.
193+
* A call to z_irq_spurious will not return.
194+
*/
195+
if (irq == 0U || irq >= 32)
196+
z_irq_spurious(NULL);
197+
198+
/*
199+
* xlnx_intc_irq_pending is returning values >= 1 but because it is second level offset
200+
* needs to be found and _sw_isr_table starting from 0 not 1.
201+
*/
202+
irq -= 1;
203+
204+
/* Apply level offset from registration as primary or secondary controller */
205+
irq += level;
206+
207+
/* Call the corresponding IRQ handler in _sw_isr_table */
208+
ite = (struct _isr_table_entry *)&_sw_isr_table[irq];
209+
210+
/* Table is already filled by z_irq_spurious calls that's why likely save to call it */
211+
ite->isr(ite->arg);
212+
213+
/* When IRQ is handled and solved by driver, irq should be ACK to interrupt controller */
214+
xlnx_intc_irq_acknowledge(irq_mask);
215+
}
216+
217+
static int xlnx_intc_init(const struct device *dev)
218+
{
219+
int32_t status = xlnx_intc_controller_init();
220+
221+
if (status != 0) {
222+
return status;
223+
}
224+
225+
#if defined(RISCV_IRQ_MEXT)
226+
/* Setup IRQ handler for PLIC driver */
227+
IRQ_CONNECT(RISCV_IRQ_MEXT, 0, xlnx_irq_handler,
228+
UINT_TO_POINTER(CONFIG_2ND_LVL_ISR_TBL_OFFSET), 0);
229+
230+
/* Enable external IRQ */
231+
irq_enable(RISCV_IRQ_MEXT);
232+
#endif
233+
234+
return xlnx_intc_irq_start();
235+
}
236+
237+
#define XILINX_INTC_INIT(inst) \
238+
DEVICE_DT_INST_DEFINE(inst, &xlnx_intc_init, NULL, NULL, NULL, \
239+
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
240+
241+
DT_INST_FOREACH_STATUS_OKAY(XILINX_INTC_INIT)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright (c) 2023-2024 Advanced Micro Devices, Inc. (AMD)
2+
# Copyright (c) 2023 Alp Sayin <[email protected]>
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
description: Xilinx AXI Interrupt Controller
6+
7+
compatible: "xlnx,xps-intc-1.00.a"
8+
9+
include: [interrupt-controller.yaml, base.yaml]
10+
11+
properties:
12+
reg:
13+
required: true
14+
15+
"#interrupt-cells":
16+
const: 2
17+
18+
xlnx,kind-of-intr:
19+
type: int
20+
required: true
21+
22+
xlnx,num-intr-inputs:
23+
type: int
24+
required: true
25+
26+
interrupt-cells:
27+
- irq
28+
- priority
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright (c) 2024 Advanced Micro Devices, Inc. (AMD)
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_DRIVERS_INTC_XLNX_H_
8+
#define ZEPHYR_INCLUDE_DRIVERS_INTC_XLNX_H_
9+
10+
#ifndef _ASMLANGUAGE
11+
extern void xlnx_intc_irq_enable(uint32_t irq);
12+
extern void xlnx_intc_irq_disable(uint32_t irq);
13+
extern void xlnx_intc_irq_acknowledge(uint32_t mask);
14+
extern uint32_t xlnx_intc_irq_pending(void);
15+
extern uint32_t xlnx_intc_irq_get_enabled(void);
16+
extern uint32_t xlnx_intc_irq_pending_vector(void);
17+
#endif /* _ASMLANGUAGE */
18+
19+
#endif /* ZEPHYR_INCLUDE_DRIVERS_INTC_XLNX_H_ */

0 commit comments

Comments
 (0)