Skip to content

Commit d060b89

Browse files
committed
drivers: interrupt_controller: add a WCH EXTI external interrupt driver
Signed-off-by: Michael Hope <michaelh@juju.nz>
1 parent 81ee157 commit d060b89

File tree

8 files changed

+238
-0
lines changed

8 files changed

+238
-0
lines changed

drivers/interrupt_controller/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ zephyr_library_sources_ifdef(CONFIG_RENESAS_RZ_EXT_IRQ intc_renesas_rz_ext_
4545
zephyr_library_sources_ifdef(CONFIG_NXP_IRQSTEER intc_nxp_irqsteer.c)
4646
zephyr_library_sources_ifdef(CONFIG_INTC_MTK_ADSP intc_mtk_adsp.c)
4747
zephyr_library_sources_ifdef(CONFIG_WCH_PFIC intc_wch_pfic.c)
48+
zephyr_library_sources_ifdef(CONFIG_WCH_EXTI intc_wch_exti.c)
4849

4950
if(CONFIG_INTEL_VTD_ICTL)
5051
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
@@ -110,4 +110,6 @@ source "drivers/interrupt_controller/Kconfig.mtk_adsp"
110110

111111
source "drivers/interrupt_controller/Kconfig.wch_pfic"
112112

113+
source "drivers/interrupt_controller/Kconfig.wch_exti"
114+
113115
endmenu
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2025 Michael Hope <michaelh@juju.nz>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config WCH_EXTI
5+
bool "WCH CH32V003/20x/30x External Interrupt and Event Controller (EXTI)"
6+
default y
7+
depends on DT_HAS_WCH_EXTI_ENABLED
8+
help
9+
Enable the WCH CH32V003/20x/30x External Interrupt and Event Controller (EXTI)
10+
driver.
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright (c) 2025 Michael Hope <michaelh@juju.nz>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT wch_exti
8+
9+
#include <errno.h>
10+
11+
#include <zephyr/device.h>
12+
#include <zephyr/irq.h>
13+
#include <zephyr/sys/util_macro.h>
14+
#include <zephyr/drivers/interrupt_controller/wch_exti.h>
15+
16+
#include <ch32fun.h>
17+
18+
#define WCH_EXTI_NUM_LINES DT_PROP(DT_NODELABEL(exti), num_lines)
19+
20+
/* Per EXTI callback registration */
21+
struct wch_exti_registration {
22+
wch_exti_callback_handler_t callback;
23+
void *user;
24+
};
25+
26+
struct wch_exti_data {
27+
struct wch_exti_registration callbacks[WCH_EXTI_NUM_LINES];
28+
};
29+
30+
#define WCH_EXTI_INIT_RANGE(node_id, interrupts, idx) \
31+
DT_PROP_BY_IDX(node_id, line_ranges, UTIL_X2(idx)),
32+
33+
/* [start, end) line ranges for each line group */
34+
static const uint8_t wch_exti_ranges[] = {
35+
DT_FOREACH_PROP_ELEM(DT_NODELABEL(exti), interrupt_names, WCH_EXTI_INIT_RANGE)
36+
WCH_EXTI_NUM_LINES,
37+
};
38+
39+
#define WCH_EXTI_INIT_INTERRUPT(node_id, interrupts, idx) DT_IRQ_BY_IDX(node_id, idx, irq),
40+
41+
/* Interrupt number for each line group. Used when enabling the interrupt. */
42+
const uint8_t wch_exti_interrupts[] = {
43+
DT_FOREACH_PROP_ELEM(DT_NODELABEL(exti), interrupt_names, WCH_EXTI_INIT_INTERRUPT)};
44+
45+
BUILD_ASSERT(ARRAY_SIZE(wch_exti_interrupts) + 1 == ARRAY_SIZE(wch_exti_ranges));
46+
47+
static void wch_exti_isr(const void *user)
48+
{
49+
const struct device *const dev = DEVICE_DT_INST_GET(0);
50+
struct wch_exti_data *data = dev->data;
51+
EXTI_TypeDef *regs = (EXTI_TypeDef *)DT_INST_REG_ADDR(0);
52+
const uint8_t *range = user;
53+
uint32_t intfr = regs->INTFR;
54+
55+
for (uint8_t line = range[0]; line < range[1]; line++) {
56+
if ((intfr & BIT(line)) != 0) {
57+
const struct wch_exti_registration *callback = &data->callbacks[line];
58+
/* Clear the interrupt */
59+
regs->INTFR = BIT(line);
60+
if (callback->callback != NULL) {
61+
callback->callback(line, callback->user);
62+
}
63+
}
64+
}
65+
}
66+
67+
void wch_exti_enable(uint8_t line)
68+
{
69+
EXTI_TypeDef *regs = (EXTI_TypeDef *)DT_INST_REG_ADDR(0);
70+
71+
regs->INTENR |= BIT(line);
72+
/* Find the corresponding interrupt and enable it */
73+
for (uint8_t i = 1; i < ARRAY_SIZE(wch_exti_ranges); i++) {
74+
if (line < wch_exti_ranges[i]) {
75+
irq_enable(wch_exti_interrupts[i - 1]);
76+
break;
77+
}
78+
}
79+
}
80+
81+
void wch_exti_disable(uint8_t line)
82+
{
83+
EXTI_TypeDef *regs = (EXTI_TypeDef *)DT_INST_REG_ADDR(0);
84+
regs->INTENR &= ~BIT(line);
85+
}
86+
87+
int wch_exti_configure(uint8_t line, wch_exti_callback_handler_t callback, void *user)
88+
{
89+
const struct device *const dev = DEVICE_DT_INST_GET(0);
90+
struct wch_exti_data *data = dev->data;
91+
struct wch_exti_registration *registration = &data->callbacks[line];
92+
93+
if (callback != NULL && registration->callback != NULL) {
94+
return -EALREADY;
95+
}
96+
97+
registration->callback = callback;
98+
registration->user = user;
99+
100+
return 0;
101+
}
102+
103+
void wch_exti_set_trigger(uint8_t line, enum wch_exti_trigger trigger)
104+
{
105+
EXTI_TypeDef *regs = (EXTI_TypeDef *)DT_INST_REG_ADDR(0);
106+
107+
WRITE_BIT(regs->RTENR, line, (trigger & WCH_EXTI_TRIGGER_RISING_EDGE) != 0);
108+
WRITE_BIT(regs->FTENR, line, (trigger & WCH_EXTI_TRIGGER_FALLING_EDGE) != 0);
109+
}
110+
111+
#define WCH_EXTI_CONNECT_IRQ(node_id, interrupts, idx) \
112+
IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), DT_IRQ_BY_IDX(node_id, idx, priority), \
113+
wch_exti_isr, &wch_exti_ranges[idx], 0);
114+
115+
static int wch_exti_init(const struct device *dev)
116+
{
117+
DT_FOREACH_PROP_ELEM(DT_NODELABEL(exti), interrupt_names, WCH_EXTI_CONNECT_IRQ);
118+
119+
return 0;
120+
}
121+
122+
static struct wch_exti_data wch_exti_data_0;
123+
124+
DEVICE_DT_INST_DEFINE(0, wch_exti_init, NULL, &wch_exti_data_0, NULL, PRE_KERNEL_2,
125+
CONFIG_INTC_INIT_PRIORITY, NULL);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright (c) 2025 Michael Hope
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: WCH CH32V003/20x/30x External Interrupt and Event Controller (EXTI)
5+
6+
compatible: "wch,exti"
7+
8+
include: [base.yaml, interrupt-controller.yaml]
9+
10+
properties:
11+
reg:
12+
required: true
13+
14+
interrupts:
15+
required: true
16+
17+
num-lines:
18+
type: int
19+
required: true
20+
description: Number of lines supported by the interrupt controller.
21+
22+
line-ranges:
23+
type: array
24+
required: true
25+
description: |
26+
Description of the input lines range for each interrupt line supported
27+
by the external interrupt controller. For each line a couple of integers is
28+
provided: the number of the first line of the range start and the length
29+
of the range.
30+
As example:
31+
line-ranges = <0 1>, <1 1>, <2 1>, <3 1>,
32+
<4 1>, <5 5>, <10 6>;
33+
Above property provides event-range for 7 lines.
34+
5 first lines contain one element
35+
6th line starts with input line 5 and contains 5 elements (5 to 9)
36+
7th line starts with inupt line 10 and contains 6 elements (10 to 15)

dts/riscv/wch/ch32v0/ch32v003.dtsi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@
7272
status = "disabled";
7373
};
7474

75+
exti: interrupt-controller@40010400 {
76+
compatible = "wch,exti";
77+
interrupt-controller;
78+
#interrupt-cells = <1>;
79+
reg = <0x40010400 16>;
80+
interrupt-parent = <&pfic>;
81+
num-lines = <8>;
82+
interrupts = <20>;
83+
line-ranges = <0 8>;
84+
interrupt-names = "line0-7";
85+
status = "disabled";
86+
};
87+
7588
pinctrl: pin-controller@40010000 {
7689
compatible = "wch,afio";
7790
reg = <0x40010000 0x10>;

dts/riscv/wch/ch32v208/ch32v208.dtsi

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,21 @@
7171
status = "disabled";
7272
};
7373

74+
exti: interrupt-controller@40010400 {
75+
compatible = "wch,exti";
76+
interrupt-controller;
77+
#interrupt-cells = <1>;
78+
reg = <0x40010400 16>;
79+
interrupt-parent = <&pfic>;
80+
num-lines = <16>;
81+
line-ranges = <0 1>, <1 1>, <2 1>, <3 1>, <4 1>,
82+
<5 5>, <10 6>;
83+
interrupts = <22 23 24 25 26 39 56>;
84+
interrupt-names = "line0", "line1", "line2", "line3",
85+
"line4", "line5-9", "line10-15";
86+
status = "disabled";
87+
};
88+
7489
pinctrl: pin-controller@40010000 {
7590
compatible = "wch,20x_30x-afio";
7691
reg = <0x40010000 16>;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2025 Michael Hope <michaelh@juju.nz>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_DRIVERS_INTERRUPT_CONTROLLER_WCH_EXTI_H_
8+
#define ZEPHYR_INCLUDE_DRIVERS_INTERRUPT_CONTROLLER_WCH_EXTI_H_
9+
10+
#include <stdint.h>
11+
12+
#include <zephyr/sys/util_macro.h>
13+
14+
/* Callback for EXTI interrupt. */
15+
typedef void (*wch_exti_callback_handler_t)(uint8_t line, void *user);
16+
17+
enum wch_exti_trigger {
18+
/* Trigger on rising edge */
19+
WCH_EXTI_TRIGGER_RISING_EDGE = BIT(0),
20+
/* Trigger on falling edge */
21+
WCH_EXTI_TRIGGER_FALLING_EDGE = BIT(1),
22+
};
23+
24+
/* Eanble the EXTI interrupt for `line` */
25+
void wch_exti_enable(uint8_t line);
26+
27+
/* Disable the EXTI interrupt for `line` */
28+
void wch_exti_disable(uint8_t line);
29+
30+
/* Set the trigger mode for `line` */
31+
void wch_exti_set_trigger(uint8_t line, enum wch_exti_trigger trigger);
32+
33+
/* Register a callback for `line` */
34+
int wch_exti_configure(uint8_t line, wch_exti_callback_handler_t callback, void *user);
35+
36+
#endif /* ZEPHYR_INCLUDE_DRIVERS_INTERRUPT_CONTROLLER_WCH_EXTI_H_ */

0 commit comments

Comments
 (0)