Skip to content

Commit 33f55b1

Browse files
committed
drivers: gpio: add interrupt support for the CH32V family
Signed-off-by: Michael Hope <michaelh@juju.nz>
1 parent d060b89 commit 33f55b1

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

drivers/gpio/Kconfig.wch_ch32v00x

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,11 @@ 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. Disabling saves approximately 700 bytes of flash and 60 bytes of RAM.

drivers/gpio/wch_gpio_ch32v00x.c

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

123248
static int gpio_ch32v00x_init(const struct device *dev)

0 commit comments

Comments
 (0)