Skip to content

Commit 8f738ab

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 e93daca commit 8f738ab

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-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: 126 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,137 @@ 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+
default:
151+
return -EINVAL;
152+
}
153+
154+
#if defined(AFIO_EXTICR_EXTI0)
155+
/* CH32V003 style with one register with 2 bits per map. */
156+
BUILD_ASSERT(AFIO_EXTICR_EXTI0 == 0x03);
157+
158+
(void)cr_id;
159+
bit0 = pin << 1;
160+
afio->EXTICR = (afio->EXTICR & ~(AFIO_EXTICR_EXTI0 << bit0)) | (port_id << bit0);
161+
#elif defined(AFIO_EXTICR1_EXTI0)
162+
/*
163+
* CH32V20x style with multiple registers with 4 pins per register and 4 bits per
164+
* map.
165+
*/
166+
BUILD_ASSERT(AFIO_EXTICR1_EXTI0 == 0x0F);
167+
BUILD_ASSERT(ARRAY_SIZE(afio->EXTICR) == 4);
168+
169+
cr_id = pin / 4;
170+
bit0 = (pin % 4) << 4;
171+
afio->EXTICR[cr_id] =
172+
(afio->EXTICR[cr_id] & ~(AFIO_EXTICR1_EXTI0 << bit0)) | (port_id << bit0);
173+
#else
174+
#error Unrecognised EXTICR format
175+
#endif
176+
177+
return 0;
178+
}
179+
180+
static int gpio_ch32v00x_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
181+
enum gpio_int_mode mode,
182+
enum gpio_int_trig trigger)
183+
{
184+
int err;
185+
186+
switch (mode) {
187+
case GPIO_INT_MODE_DISABLED:
188+
wch_exti_disable(pin);
189+
err = wch_exti_configure(pin, NULL, NULL);
190+
break;
191+
case GPIO_INT_MODE_EDGE:
192+
err = wch_exti_configure(pin, gpio_ch32v00x_isr, (void *)dev);
193+
if (err != 0) {
194+
break;
195+
}
196+
197+
err = gpio_ch32v00x_configure_exti(dev, pin);
198+
if (err != 0) {
199+
break;
200+
}
201+
202+
switch (trigger) {
203+
case GPIO_INT_TRIG_LOW:
204+
wch_exti_set_trigger(pin, WCH_EXTI_TRIGGER_FALLING_EDGE);
205+
break;
206+
case GPIO_INT_TRIG_HIGH:
207+
wch_exti_set_trigger(pin, WCH_EXTI_TRIGGER_RISING_EDGE);
208+
break;
209+
case GPIO_INT_TRIG_BOTH:
210+
wch_exti_set_trigger(pin, WCH_EXTI_TRIGGER_FALLING_EDGE |
211+
WCH_EXTI_TRIGGER_RISING_EDGE);
212+
break;
213+
default:
214+
return -ENOTSUP;
215+
}
216+
217+
wch_exti_enable(pin);
218+
break;
219+
default:
220+
return -ENOTSUP;
221+
}
222+
223+
return err;
224+
}
225+
226+
static int gpio_ch32v00x_manage_callback(const struct device *dev, struct gpio_callback *callback,
227+
bool set)
228+
{
229+
struct gpio_ch32v00x_data *data = dev->data;
230+
231+
return gpio_manage_callback(&data->callbacks, callback, set);
232+
}
233+
234+
#endif /* CONFIG_GPIO_WCH_GPIO_INTERRUPTS */
235+
114236
static DEVICE_API(gpio, gpio_ch32v00x_driver_api) = {
115237
.pin_configure = gpio_ch32v00x_configure,
116238
.port_get_raw = gpio_ch32v00x_port_get_raw,
117239
.port_set_masked_raw = gpio_ch32v00x_port_set_masked_raw,
118240
.port_set_bits_raw = gpio_ch32v00x_port_set_bits_raw,
119241
.port_clear_bits_raw = gpio_ch32v00x_port_clear_bits_raw,
120242
.port_toggle_bits = gpio_ch32v00x_port_toggle_bits,
243+
#if defined(CONFIG_GPIO_WCH_GPIO_INTERRUPTS)
244+
.pin_interrupt_configure = gpio_ch32v00x_pin_interrupt_configure,
245+
.manage_callback = gpio_ch32v00x_manage_callback,
246+
#endif
121247
};
122248

123249
static int gpio_ch32v00x_init(const struct device *dev)

0 commit comments

Comments
 (0)