Skip to content

Commit c52e395

Browse files
committed
drivers: interrupt_controller: add tic interrupt-controller driver
Add interrupt-controller driver for tic, required by TOPST VCP45. Signed-off-by: Hounjoung Rim <[email protected]>
1 parent 9a04927 commit c52e395

File tree

6 files changed

+496
-0
lines changed

6 files changed

+496
-0
lines changed

drivers/interrupt_controller/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ zephyr_library_sources_ifdef(CONFIG_NXP_IRQSTEER intc_nxp_irqsteer.c)
4747
zephyr_library_sources_ifdef(CONFIG_INTC_MTK_ADSP intc_mtk_adsp.c)
4848
zephyr_library_sources_ifdef(CONFIG_WCH_PFIC intc_wch_pfic.c)
4949
zephyr_library_sources_ifdef(CONFIG_WCH_EXTI intc_wch_exti.c)
50+
zephyr_library_sources_ifdef(CONFIG_TIC intc_tic.c)
5051

5152
if(CONFIG_INTEL_VTD_ICTL)
5253
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
@@ -114,4 +114,6 @@ source "drivers/interrupt_controller/Kconfig.wch_pfic"
114114

115115
source "drivers/interrupt_controller/Kconfig.wch_exti"
116116

117+
source "drivers/interrupt_controller/Kconfig.tic"
118+
117119
endmenu
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright 2024 Hounjoung Rim <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
if CPU_CORTEX_R5
5+
6+
config TIC
7+
bool "Telechips Interrupt Controller"
8+
default y
9+
depends on DT_HAS_TCC_TIC_ENABLED
10+
help
11+
The Telechips Interrupt Controller provides hardware assistance for prioritizing
12+
and aggregating the interrupt sources for ARM Cortex-R5 processor cores.
13+
14+
endif
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/*
2+
* Copyright (c) 2024 Hounjoung Rim <[email protected]>
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
/*
7+
* NOTE: This driver implements the GIC400 interfaces.
8+
*/
9+
10+
#define DT_DRV_COMPAT tcc_tic
11+
12+
#include <stdint.h>
13+
14+
#include <zephyr/arch/arm/irq.h>
15+
#include <zephyr/arch/cpu.h>
16+
#include <zephyr/devicetree.h>
17+
#include <zephyr/kernel.h>
18+
#include <zephyr/logging/log.h>
19+
#include <zephyr/sys/util_macro.h>
20+
21+
#include <zephyr/dt-bindings/interrupt-controller/tcc-tic.h>
22+
#include <zephyr/drivers/interrupt_controller/intc_tic.h>
23+
24+
LOG_MODULE_REGISTER(tic);
25+
26+
static tic_irq_func_ptr tic_intr_table[TIC_INT_SRC_CNT];
27+
28+
static void tic_irq_pri_set_internal(uint32_t irq, uint32_t pri)
29+
{
30+
uint32_t reg_offset, reg_bits, intr_pri_reg;
31+
32+
reg_offset = 0;
33+
reg_bits = 0;
34+
intr_pri_reg = 0;
35+
36+
if ((pri < TIC_PRIORITY_NO_MEAN) && (irq < TIC_INT_SRC_CNT)) {
37+
reg_offset = (irq >> 2u);
38+
reg_bits = (irq & 0x03u);
39+
40+
intr_pri_reg = tic_distributer->dist_intr_pri[reg_offset];
41+
intr_pri_reg = (uint32_t)(intr_pri_reg & ~(0xFFu << (reg_bits * 8u)));
42+
intr_pri_reg = (uint32_t)(intr_pri_reg | ((pri & 0xFFu) << (reg_bits * 8u)));
43+
44+
tic_distributer->dist_intr_pri[reg_offset] = intr_pri_reg;
45+
}
46+
}
47+
48+
static void tic_irq_config_set(uint32_t irq, uint8_t irq_type)
49+
{
50+
uint32_t reg_offset, reg_mask, intr_config;
51+
52+
reg_offset = 0;
53+
reg_mask = 0;
54+
intr_config = 0;
55+
56+
if (irq < TIC_INT_SRC_CNT) {
57+
reg_offset = (irq >> 4u);
58+
reg_mask = (uint32_t)(0x2u << ((irq & 0xfu) * 2u));
59+
intr_config = tic_distributer->dist_intr_config[reg_offset];
60+
61+
if (((irq_type & (uint8_t)TIC_INT_TYPE_LEVEL_HIGH) ==
62+
(uint8_t)TIC_INT_TYPE_LEVEL_HIGH) ||
63+
((irq_type & (uint8_t)TIC_INT_TYPE_LEVEL_LOW) ==
64+
(uint8_t)TIC_INT_TYPE_LEVEL_LOW)) {
65+
intr_config = (uint32_t)(intr_config & ~reg_mask);
66+
} else {
67+
intr_config = (uint32_t)(intr_config | reg_mask);
68+
}
69+
70+
tic_distributer->dist_intr_config[reg_offset] = intr_config;
71+
}
72+
}
73+
74+
void tic_irq_vector_set(uint32_t irq, uint32_t pri, uint8_t irq_type, tic_isr_func irq_func,
75+
void *irq_arg)
76+
{
77+
uint32_t rsvd_irq;
78+
79+
rsvd_irq = 0;
80+
81+
if ((pri > TIC_PRIORITY_NO_MEAN) || (irq >= TIC_INT_SRC_CNT)) {
82+
return;
83+
}
84+
85+
tic_irq_pri_set_internal(irq, pri);
86+
tic_irq_config_set(irq, irq_type);
87+
88+
tic_intr_table[irq].if_func_ptr = irq_func;
89+
tic_intr_table[irq].if_arg_ptr = irq_arg;
90+
tic_intr_table[irq].if_is_both_edge = 0;
91+
92+
if ((irq >= (uint32_t)TIC_EINT_START_INT) &&
93+
(irq <= (uint32_t)TIC_EINT_END_INT) /* Set reversed external interrupt */
94+
&& (irq_type == (uint8_t)TIC_INT_TYPE_EDGE_BOTH)) { /* for supporting both edge. */
95+
96+
rsvd_irq = (irq + TIC_EINT_NUM); /* add offset of IRQn */
97+
98+
tic_irq_pri_set_internal(rsvd_irq, pri);
99+
tic_irq_config_set(rsvd_irq, irq_type);
100+
101+
tic_intr_table[rsvd_irq].if_func_ptr = irq_func;
102+
tic_intr_table[rsvd_irq].if_arg_ptr = irq_arg;
103+
tic_intr_table[irq].if_is_both_edge = (1U);
104+
}
105+
}
106+
107+
unsigned int z_tic_irq_get_active(void)
108+
{
109+
uint32_t int_id;
110+
111+
int_id = tic_cpu_if->cpu_intr_ack;
112+
113+
return int_id;
114+
}
115+
116+
void z_tic_irq_eoi(unsigned int irq)
117+
{
118+
tic_cpu_if->cpu_end_intr = irq;
119+
}
120+
121+
void z_tic_irq_init(void)
122+
{
123+
unsigned long reg_offset;
124+
125+
reg_offset = 0;
126+
127+
/* Global TIC disable -> enable. */
128+
tic_distributer->dist_ctrl &= (unsigned long)(~ARM_BIT_TIC_DIST_ICDDCR_EN);
129+
tic_distributer->dist_ctrl |= (unsigned long)ARM_BIT_TIC_DIST_ICDDCR_EN;
130+
131+
for (; reg_offset <= ((unsigned long)(TIC_INT_SRC_CNT - 1UL) / 4UL); reg_offset++) {
132+
tic_distributer->dist_intr_pri[reg_offset] = 0xFAFAFAFAUL;
133+
}
134+
135+
tic_cpu_if->cpu_pri_mask = UNMASK_VALUE;
136+
tic_cpu_if->cpu_ctlr |=
137+
(TIC_CPUIF_CTRL_ENABLEGRP0 | TIC_CPUIF_CTRL_ENABLEGRP1 | TIC_CPUIF_CTRL_ACKCTL);
138+
139+
LOG_DBG("TIC: Number of IRQs = %lu\n", (unsigned long)TIC_INT_SRC_CNT);
140+
}
141+
142+
void z_tic_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags)
143+
{
144+
tic_irq_pri_set_internal(irq, prio);
145+
}
146+
147+
void z_tic_irq_enable(unsigned int irq)
148+
{
149+
uint32_t reg_offset, mask_bit_id;
150+
151+
reg_offset = 0;
152+
mask_bit_id = 0;
153+
154+
if (irq < TIC_INT_SRC_CNT) {
155+
reg_offset = (irq >> 5u); /* Calculate the register offset. */
156+
mask_bit_id = (irq & 0x1Fu); /* Mask bit ID. */
157+
158+
tic_distributer->dist_intr_set_en[reg_offset] = (1UL << mask_bit_id);
159+
160+
if (tic_intr_table[irq].if_is_both_edge == (1UL)) {
161+
reg_offset = ((irq + 10UL) >> 5UL); /* Calculate the register offset. */
162+
mask_bit_id = ((irq + 10UL) & 0x1FUL); /* Mask bit ID. */
163+
164+
tic_distributer->dist_intr_set_en[reg_offset] = (1UL << mask_bit_id);
165+
}
166+
} else {
167+
LOG_ERR("%s: Invalid irq number = %u\n", __func__, irq);
168+
return;
169+
}
170+
}
171+
172+
void z_tic_irq_disable(unsigned int irq)
173+
{
174+
uint32_t reg_offset, mask_bit_id;
175+
176+
reg_offset = 0;
177+
mask_bit_id = 0;
178+
179+
if (irq < TIC_INT_SRC_CNT) {
180+
reg_offset = (irq >> 5UL); /* Calculate the register offset. */
181+
mask_bit_id = (irq & 0x1FUL); /* Mask bit ID. */
182+
183+
tic_distributer->dist_intr_clr_en[reg_offset] = (1UL << mask_bit_id);
184+
185+
if (tic_intr_table[irq].if_is_both_edge == (1UL)) {
186+
reg_offset = ((irq + 10UL) >> 5UL); /* Calculate the register offset. */
187+
mask_bit_id = ((irq + 10UL) & 0x1FUL); /* Mask bit ID. */
188+
189+
tic_distributer->dist_intr_clr_en[reg_offset] = (1UL << mask_bit_id);
190+
}
191+
} else {
192+
LOG_ERR("%s: Invalid irq number = %u\n", __func__, irq);
193+
return;
194+
}
195+
}
196+
197+
bool z_tic_irq_is_enabled(unsigned int irq)
198+
{
199+
uint32_t reg_offset, mask_bit_id, enabler;
200+
201+
reg_offset = 0;
202+
mask_bit_id = 0;
203+
204+
if (irq < TIC_INT_SRC_CNT) {
205+
reg_offset = (irq >> 5u); /* Calculate the register offset. */
206+
mask_bit_id = (irq & 0x1Fu); /* Mask bit ID. */
207+
208+
enabler = tic_distributer->dist_intr_set_en[reg_offset];
209+
210+
return (enabler & (1 << mask_bit_id)) != 0;
211+
}
212+
213+
LOG_ERR("%s: Invalid irq number = %u\n", __func__, irq);
214+
return false;
215+
}
216+
217+
void z_tic_arm_enter_irq(int irq)
218+
{
219+
uint32_t target_list_filter;
220+
uint32_t cpu_target_list, group_id;
221+
222+
target_list_filter = TIC_SGI_TO_TARGETLIST;
223+
cpu_target_list = 0x1UL; /* bitfiled 0 : cpu #0, bitfield n : cpu #n, n : 0 ~ 7 */
224+
group_id = 0UL; /* 0 : group 0 , 1: group 1 */
225+
226+
if (irq <= 15UL) {
227+
tic_distributer->dist_sw_gen_intr = (uint32_t)((target_list_filter & 0x3UL) << 24) |
228+
((cpu_target_list & 0xffUL) << 16) |
229+
((group_id & 0x1UL) << 15) | (irq & 0xfUL);
230+
} else {
231+
LOG_ERR("%s: Invalid irq number = %u\n", __func__, irq);
232+
}
233+
}
234+
235+
void tic_irq_handler(void *arg)
236+
{
237+
uint32_t intr_ack_reg, irq;
238+
tic_isr_func func_isr;
239+
void *intr_arg_ptr;
240+
241+
intr_ack_reg = z_tic_irq_get_active();
242+
irq = intr_ack_reg & 0x3FFU; /* Mask away the CPUID. */
243+
func_isr = (tic_isr_func)NULL;
244+
intr_arg_ptr = TCC_NULL_PTR;
245+
246+
if (irq < TIC_INT_SRC_CNT) {
247+
func_isr = tic_intr_table[irq].if_func_ptr; /* Fetch ISR handler. */
248+
intr_arg_ptr = tic_intr_table[irq].if_arg_ptr;
249+
250+
if (func_isr != (tic_isr_func)NULL) {
251+
(*func_isr)(intr_arg_ptr); /* Call ISR handler. */
252+
}
253+
254+
z_tic_irq_eoi(intr_ack_reg);
255+
}
256+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright (c) 2024 Hounjoung Rim <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Telechips Generic Interrupt Controller 400
5+
6+
compatible: "tcc,tic"
7+
8+
include: base.yaml
9+
10+
properties:
11+
reg:
12+
required: true
13+
14+
interrupt-cells:
15+
- type
16+
- irq
17+
- flags
18+
- priority

0 commit comments

Comments
 (0)