Skip to content

Commit 355cd48

Browse files
committed
drivers: gpio: add interrupt support for the CH32V family
The WCH GPIO peripheral integrates with the EXTI and supports firing interrupts when a GPIO pin changes. Add optional support for firing a callback on rising edge, falling edge, or both edges. Tested on the `linkw` and the `ch32v006evt` using `samples/basic/button`. Signed-off-by: Michael Hope <michaelh@juju.nz>
1 parent 184f416 commit 355cd48

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

drivers/gpio/Kconfig.wch_ch32v00x

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,12 @@ config GPIO_WCH_GPIO
55
bool "WCH CH32V00x GPIO driver"
66
depends on DT_HAS_WCH_GPIO_ENABLED
77
default y
8+
9+
config GPIO_WCH_GPIO_INTERRUPTS
10+
bool "Interrupt support"
11+
depends on GPIO_WCH_GPIO
12+
depends on DT_HAS_WCH_EXTI_ENABLED
13+
default y
14+
help
15+
Support triggering an interrupt on pin change. Uses approximately
16+
700 bytes of flash and 60 bytes of RAM.

drivers/gpio/wch_gpio_ch32v00x.c

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <zephyr/drivers/clock_control.h>
88
#include <zephyr/drivers/gpio.h>
99
#include <zephyr/drivers/gpio/gpio_utils.h>
10+
#include <zephyr/drivers/interrupt_controller/wch_exti.h>
1011
#include <zephyr/dt-bindings/gpio/gpio.h>
1112
#include <zephyr/irq.h>
1213

@@ -23,6 +24,7 @@ struct gpio_ch32v00x_config {
2324

2425
struct gpio_ch32v00x_data {
2526
struct gpio_driver_data common;
27+
sys_slist_t callbacks;
2628
};
2729

2830
static int gpio_ch32v00x_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
@@ -111,13 +113,142 @@ static int gpio_ch32v00x_port_toggle_bits(const struct device *dev, uint32_t pin
111113
return 0;
112114
}
113115

116+
#if defined(CONFIG_GPIO_WCH_GPIO_INTERRUPTS)
117+
118+
static void gpio_ch32v00x_isr(uint8_t line, void *user)
119+
{
120+
const struct device *dev = user;
121+
struct gpio_ch32v00x_data *data = dev->data;
122+
123+
gpio_fire_callbacks(&data->callbacks, dev, BIT(line));
124+
}
125+
126+
static int gpio_ch32v00x_configure_exti(const struct device *dev, gpio_pin_t pin)
127+
{
128+
const struct gpio_ch32v00x_config *config = dev->config;
129+
AFIO_TypeDef *afio = (AFIO_TypeDef *)DT_REG_ADDR(DT_NODELABEL(pinctrl));
130+
uint8_t port_id;
131+
uint8_t cr_id;
132+
uint8_t bit0;
133+
134+
/* Convert the device into a port ID by checking the address */
135+
switch ((uintptr_t)config->regs) {
136+
case DT_REG_ADDR(DT_NODELABEL(gpioa)):
137+
port_id = 0;
138+
break;
139+
#if DT_NODE_EXISTS(DT_NODELABEL(gpiob))
140+
case DT_REG_ADDR(DT_NODELABEL(gpiob)):
141+
port_id = 1;
142+
break;
143+
#endif
144+
case DT_REG_ADDR(DT_NODELABEL(gpioc)):
145+
port_id = 2;
146+
break;
147+
case DT_REG_ADDR(DT_NODELABEL(gpiod)):
148+
port_id = 3;
149+
break;
150+
#if DT_NODE_EXISTS(DT_NODELABEL(gpioe))
151+
case DT_REG_ADDR(DT_NODELABEL(gpioe)):
152+
port_id = 4;
153+
break;
154+
#endif
155+
default:
156+
return -EINVAL;
157+
}
158+
159+
#if defined(AFIO_EXTICR_EXTI0)
160+
/* CH32V003 style with one register with 2 bits per map. */
161+
BUILD_ASSERT(AFIO_EXTICR_EXTI0 == 0x03);
162+
163+
(void)cr_id;
164+
bit0 = pin << 1;
165+
afio->EXTICR = (afio->EXTICR & ~(AFIO_EXTICR_EXTI0 << bit0)) | (port_id << bit0);
166+
#elif defined(AFIO_EXTICR1_EXTI0)
167+
/*
168+
* CH32V20x style with multiple registers with 4 pins per register and 4 bits per
169+
* map.
170+
*/
171+
BUILD_ASSERT(AFIO_EXTICR1_EXTI0 == 0x0F);
172+
BUILD_ASSERT(ARRAY_SIZE(afio->EXTICR) == 4);
173+
174+
cr_id = pin / 4;
175+
bit0 = (pin % 4) << 4;
176+
afio->EXTICR[cr_id] =
177+
(afio->EXTICR[cr_id] & ~(AFIO_EXTICR1_EXTI0 << bit0)) | (port_id << bit0);
178+
#else
179+
#error Unrecognised EXTICR format
180+
#endif
181+
182+
return 0;
183+
}
184+
185+
static int gpio_ch32v00x_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
186+
enum gpio_int_mode mode,
187+
enum gpio_int_trig trigger)
188+
{
189+
int err;
190+
191+
switch (mode) {
192+
case GPIO_INT_MODE_DISABLED:
193+
wch_exti_disable(pin);
194+
err = wch_exti_configure(pin, NULL, NULL);
195+
break;
196+
case GPIO_INT_MODE_EDGE:
197+
err = wch_exti_configure(pin, gpio_ch32v00x_isr, (void *)dev);
198+
if (err != 0) {
199+
break;
200+
}
201+
202+
err = gpio_ch32v00x_configure_exti(dev, pin);
203+
if (err != 0) {
204+
break;
205+
}
206+
207+
switch (trigger) {
208+
case GPIO_INT_TRIG_LOW:
209+
wch_exti_set_trigger(pin, WCH_EXTI_TRIGGER_FALLING_EDGE);
210+
break;
211+
case GPIO_INT_TRIG_HIGH:
212+
wch_exti_set_trigger(pin, WCH_EXTI_TRIGGER_RISING_EDGE);
213+
break;
214+
case GPIO_INT_TRIG_BOTH:
215+
wch_exti_set_trigger(pin, WCH_EXTI_TRIGGER_FALLING_EDGE |
216+
WCH_EXTI_TRIGGER_RISING_EDGE);
217+
break;
218+
default:
219+
return -ENOTSUP;
220+
}
221+
222+
wch_exti_enable(pin);
223+
break;
224+
default:
225+
return -ENOTSUP;
226+
}
227+
228+
return err;
229+
}
230+
231+
static int gpio_ch32v00x_manage_callback(const struct device *dev, struct gpio_callback *callback,
232+
bool set)
233+
{
234+
struct gpio_ch32v00x_data *data = dev->data;
235+
236+
return gpio_manage_callback(&data->callbacks, callback, set);
237+
}
238+
239+
#endif /* CONFIG_GPIO_WCH_GPIO_INTERRUPTS */
240+
114241
static DEVICE_API(gpio, gpio_ch32v00x_driver_api) = {
115242
.pin_configure = gpio_ch32v00x_configure,
116243
.port_get_raw = gpio_ch32v00x_port_get_raw,
117244
.port_set_masked_raw = gpio_ch32v00x_port_set_masked_raw,
118245
.port_set_bits_raw = gpio_ch32v00x_port_set_bits_raw,
119246
.port_clear_bits_raw = gpio_ch32v00x_port_clear_bits_raw,
120247
.port_toggle_bits = gpio_ch32v00x_port_toggle_bits,
248+
#if defined(CONFIG_GPIO_WCH_GPIO_INTERRUPTS)
249+
.pin_interrupt_configure = gpio_ch32v00x_pin_interrupt_configure,
250+
.manage_callback = gpio_ch32v00x_manage_callback,
251+
#endif
121252
};
122253

123254
static int gpio_ch32v00x_init(const struct device *dev)

0 commit comments

Comments
 (0)