Browse Source

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>
pull/92227/head
Michael Hope 2 months ago committed by Benjamin Cabé
parent
commit
37190890d6
  1. 9
      drivers/gpio/Kconfig.wch_ch32v00x
  2. 131
      drivers/gpio/wch_gpio_ch32v00x.c

9
drivers/gpio/Kconfig.wch_ch32v00x

@ -5,3 +5,12 @@ config GPIO_WCH_GPIO @@ -5,3 +5,12 @@ config GPIO_WCH_GPIO
bool "WCH CH32V00x GPIO driver"
depends on DT_HAS_WCH_GPIO_ENABLED
default y
config GPIO_WCH_GPIO_INTERRUPTS
bool "Interrupt support"
depends on GPIO_WCH_GPIO
depends on DT_HAS_WCH_EXTI_ENABLED
default y
help
Support triggering an interrupt on pin change. Uses approximately
700 bytes of flash and 60 bytes of RAM.

131
drivers/gpio/wch_gpio_ch32v00x.c

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/gpio/gpio_utils.h>
#include <zephyr/drivers/interrupt_controller/wch_exti.h>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/irq.h>
@ -23,6 +24,7 @@ struct gpio_ch32v00x_config { @@ -23,6 +24,7 @@ struct gpio_ch32v00x_config {
struct gpio_ch32v00x_data {
struct gpio_driver_data common;
sys_slist_t callbacks;
};
static int gpio_ch32v00x_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
@ -111,6 +113,131 @@ static int gpio_ch32v00x_port_toggle_bits(const struct device *dev, uint32_t pin @@ -111,6 +113,131 @@ static int gpio_ch32v00x_port_toggle_bits(const struct device *dev, uint32_t pin
return 0;
}
#if defined(CONFIG_GPIO_WCH_GPIO_INTERRUPTS)
static void gpio_ch32v00x_isr(uint8_t line, void *user)
{
const struct device *dev = user;
struct gpio_ch32v00x_data *data = dev->data;
gpio_fire_callbacks(&data->callbacks, dev, BIT(line));
}
static int gpio_ch32v00x_configure_exti(const struct device *dev, gpio_pin_t pin)
{
const struct gpio_ch32v00x_config *config = dev->config;
AFIO_TypeDef *afio = (AFIO_TypeDef *)DT_REG_ADDR(DT_NODELABEL(pinctrl));
uint8_t port_id;
uint8_t cr_id;
uint8_t bit0;
/* Convert the device into a port ID by checking the address */
switch ((uintptr_t)config->regs) {
case DT_REG_ADDR(DT_NODELABEL(gpioa)):
port_id = 0;
break;
#if DT_NODE_EXISTS(DT_NODELABEL(gpiob))
case DT_REG_ADDR(DT_NODELABEL(gpiob)):
port_id = 1;
break;
#endif
case DT_REG_ADDR(DT_NODELABEL(gpioc)):
port_id = 2;
break;
case DT_REG_ADDR(DT_NODELABEL(gpiod)):
port_id = 3;
break;
#if DT_NODE_EXISTS(DT_NODELABEL(gpioe))
case DT_REG_ADDR(DT_NODELABEL(gpioe)):
port_id = 4;
break;
#endif
default:
return -EINVAL;
}
#if defined(AFIO_EXTICR_EXTI0)
/* CH32V003 style with one register with 2 bits per map. */
BUILD_ASSERT(AFIO_EXTICR_EXTI0 == 0x03);
(void)cr_id;
bit0 = pin << 1;
afio->EXTICR = (afio->EXTICR & ~(AFIO_EXTICR_EXTI0 << bit0)) | (port_id << bit0);
#elif defined(AFIO_EXTICR1_EXTI0)
/*
* CH32V20x style with multiple registers with 4 pins per register and 4 bits per
* map.
*/
BUILD_ASSERT(AFIO_EXTICR1_EXTI0 == 0x0F);
BUILD_ASSERT(ARRAY_SIZE(afio->EXTICR) == 4);
cr_id = pin / 4;
bit0 = (pin % 4) * 4;
afio->EXTICR[cr_id] =
(afio->EXTICR[cr_id] & ~(AFIO_EXTICR1_EXTI0 << bit0)) | (port_id << bit0);
#else
#error Unrecognised EXTICR format
#endif
return 0;
}
static int gpio_ch32v00x_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
enum gpio_int_mode mode,
enum gpio_int_trig trigger)
{
int err;
switch (mode) {
case GPIO_INT_MODE_DISABLED:
wch_exti_disable(pin);
err = wch_exti_configure(pin, NULL, NULL);
break;
case GPIO_INT_MODE_EDGE:
err = wch_exti_configure(pin, gpio_ch32v00x_isr, (void *)dev);
if (err != 0) {
break;
}
err = gpio_ch32v00x_configure_exti(dev, pin);
if (err != 0) {
break;
}
switch (trigger) {
case GPIO_INT_TRIG_LOW:
wch_exti_set_trigger(pin, WCH_EXTI_TRIGGER_FALLING_EDGE);
break;
case GPIO_INT_TRIG_HIGH:
wch_exti_set_trigger(pin, WCH_EXTI_TRIGGER_RISING_EDGE);
break;
case GPIO_INT_TRIG_BOTH:
wch_exti_set_trigger(pin, WCH_EXTI_TRIGGER_FALLING_EDGE |
WCH_EXTI_TRIGGER_RISING_EDGE);
break;
default:
return -ENOTSUP;
}
wch_exti_enable(pin);
break;
default:
return -ENOTSUP;
}
return err;
}
static int gpio_ch32v00x_manage_callback(const struct device *dev, struct gpio_callback *callback,
bool set)
{
struct gpio_ch32v00x_data *data = dev->data;
return gpio_manage_callback(&data->callbacks, callback, set);
}
#endif /* CONFIG_GPIO_WCH_GPIO_INTERRUPTS */
static DEVICE_API(gpio, gpio_ch32v00x_driver_api) = {
.pin_configure = gpio_ch32v00x_configure,
.port_get_raw = gpio_ch32v00x_port_get_raw,
@ -118,6 +245,10 @@ static DEVICE_API(gpio, gpio_ch32v00x_driver_api) = { @@ -118,6 +245,10 @@ static DEVICE_API(gpio, gpio_ch32v00x_driver_api) = {
.port_set_bits_raw = gpio_ch32v00x_port_set_bits_raw,
.port_clear_bits_raw = gpio_ch32v00x_port_clear_bits_raw,
.port_toggle_bits = gpio_ch32v00x_port_toggle_bits,
#if defined(CONFIG_GPIO_WCH_GPIO_INTERRUPTS)
.pin_interrupt_configure = gpio_ch32v00x_pin_interrupt_configure,
.manage_callback = gpio_ch32v00x_manage_callback,
#endif
};
static int gpio_ch32v00x_init(const struct device *dev)

Loading…
Cancel
Save