diff --git a/cpu/gd32v/Kconfig b/cpu/gd32v/Kconfig index ec50dd6ccd025..6d3e2f5a9c56e 100644 --- a/cpu/gd32v/Kconfig +++ b/cpu/gd32v/Kconfig @@ -11,6 +11,7 @@ config CPU_FAM_GD32V select HAS_CPU_GD32V select HAS_PERIPH_CLIC select HAS_PERIPH_GPIO + select HAS_PERIPH_GPIO_IRQ select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE diff --git a/cpu/gd32v/Makefile.features b/cpu/gd32v/Makefile.features index 3bca58c77f6e1..c0473656a5f24 100644 --- a/cpu/gd32v/Makefile.features +++ b/cpu/gd32v/Makefile.features @@ -2,6 +2,7 @@ CPU_CORE := rv32imac FEATURES_PROVIDED += periph_clic FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_gpio_irq FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_timer_periodic diff --git a/cpu/gd32v/include/periph_cpu.h b/cpu/gd32v/include/periph_cpu.h index df4bae1e711b9..3f4fca0afc011 100644 --- a/cpu/gd32v/include/periph_cpu.h +++ b/cpu/gd32v/include/periph_cpu.h @@ -94,6 +94,7 @@ typedef uint32_t gpio_t; * - bit 4: output type (0: push-pull, 1: open-drain) */ #define GPIO_MODE(io, pr, ot) ((io << 0) | (pr << 2) | (ot << 4)) + /** * @brief Override GPIO mode options * @@ -110,6 +111,18 @@ typedef enum { GPIO_OD_PU = (0xff) /**< not supported by HW */ } gpio_mode_t; /** @} */ + +/** + * @brief Override flank configuration values + * @{ + */ +#define HAVE_GPIO_FLANK_T +typedef enum { + GPIO_RISING = 1, /**< emit interrupt on rising flank */ + GPIO_FALLING = 2, /**< emit interrupt on falling flank */ + GPIO_BOTH = 3 /**< emit interrupt on both flanks */ +} gpio_flank_t; +/** @} */ #endif /* ndef DOXYGEN */ /** diff --git a/cpu/gd32v/periph/gpio.c b/cpu/gd32v/periph/gpio.c index baa59fd075638..5b53b95ee424d 100644 --- a/cpu/gd32v/periph/gpio.c +++ b/cpu/gd32v/periph/gpio.c @@ -1,5 +1,7 @@ /* - * Copyright (C) 2020 Koen Zandberg + * Copyright (C) 2014-2015 Freie Universität Berlin + * 2020 Koen Zandberg + * 2023 Gunar Schorcht * * This file is subject to the terms and conditions of the GNU Lesser General * Public License v2.1. See the file LICENSE in the top level directory for more @@ -10,13 +12,19 @@ * @{ * * @file - * @brief GD32V GPIO implementation + * @brief Low-level GPIO driver implementation for GD32V * * @author Koen Zandberg + * @author Hauke Petersen + * @author Thomas Eichinger + * @author Gunar Schorcht */ +#include "assert.h" +#include "bitarithm.h" #include "cpu.h" #include "clic.h" +#include "log.h" #include "periph_cpu.h" #include "periph/gpio.h" @@ -26,6 +34,19 @@ #define MODE_MASK (0x0f) #define ODR_POS (4U) +#ifdef MODULE_PERIPH_GPIO_IRQ +/** + * @brief Number of available external interrupt lines + */ +#define GPIO_ISR_CHAN_NUMOF (16U) +#define GPIO_ISR_CHAN_MASK (0xFFFF) + +/** + * @brief Allocate memory for one callback and argument per EXTI channel + */ +static gpio_isr_ctx_t exti_ctx[GPIO_ISR_CHAN_NUMOF]; +#endif /* MODULE_PERIPH_GPIO_IRQ */ + /** * @brief Extract the port base address from the given pin identifier */ @@ -184,4 +205,116 @@ void gpio_write(gpio_t pin, int value) } } +#ifdef MODULE_PERIPH_GPIO_IRQ + +/* Forward declaration of ISR */ +static void _gpio_isr(unsigned irqn); + +static inline unsigned _irq_num(unsigned pin_num) +{ + if (pin_num < 5) { + return EXTI0_IRQn + pin_num; + } + if (pin_num < 10) { + return EXTI5_9_IRQn; + } + return EXTI10_15_IRQn; +} + +#ifndef NDEBUG +uint8_t exti_line_port[GPIO_ISR_CHAN_NUMOF]; +#endif + +int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, + gpio_cb_t cb, void *arg) +{ + assert(cb != NULL); + + int pin_num = _pin_num(pin); + int port_num = _port_num(pin); + + /* disable interrupts on the channel we want to edit (just in case) */ + EXTI->INTEN &= ~(1 << pin_num); + + /* configure pin as input */ + gpio_init(pin, mode); + +#ifndef NDEBUG + /* GD32V has 16 EXTI lines for GPIO interrupts, where all ports share + * the same EXTI line for the same pin. That means the pin PA shares + * the EXTI line with PB, PC, PD and PE. */ + if ((exti_ctx[pin_num].cb != 0) && (exti_line_port[pin_num] != port_num)) { + LOG_ERROR("EXTI line for GPIO_PIN(%u, %u) is used by GPIO_PIN(%u, %u).\n", + port_num, pin_num, exti_line_port[pin_num], pin_num); + assert(0); + } + exti_line_port[pin_num] = port_num; +#endif + + /* set callback */ + exti_ctx[pin_num].cb = cb; + exti_ctx[pin_num].arg = arg; + + /* enable alternate function clock for the GPIO module */ + periph_clk_en(APB2, RCU_APB2EN_AFEN_Msk); + + /* configure the EXTI channel */ + volatile uint32_t *afio_exti_ss = &AFIO->EXTISS0 + (pin_num >> 2); + + *afio_exti_ss &= ~(0xfUL << ((pin_num & 0x03) * 4)); + *afio_exti_ss |= (uint32_t)port_num << ((pin_num & 0x03) * 4); + + /* configure the active flank */ + EXTI->RTEN &= ~(1 << pin_num); + EXTI->RTEN |= ((flank & 0x1) << pin_num); + EXTI->FTEN &= ~(1 << pin_num); + EXTI->FTEN |= ((flank >> 1) << pin_num); + + /* clear any pending requests */ + EXTI->PD = (1 << pin_num); + + /* enable global pin interrupt */ + unsigned irqn = _irq_num(pin_num); + + clic_set_handler(irqn, _gpio_isr); + clic_enable_interrupt(irqn, CPU_DEFAULT_IRQ_PRIO); + + /* unmask the pins interrupt channel */ + EXTI->INTEN |= (1 << pin_num); + + return 0; +} + +void gpio_irq_enable(gpio_t pin) +{ + EXTI->INTEN |= (1 << _pin_num(pin)); +} + +void gpio_irq_disable(gpio_t pin) +{ + EXTI->INTEN &= ~(1 << _pin_num(pin)); +} + +static void _gpio_isr(unsigned irqn) +{ + (void)irqn; + + /* read all pending interrupts wired to isr_exti */ + uint32_t pending_isr = EXTI->PD & GPIO_ISR_CHAN_MASK; + + /* clear by writing a 1 */ + EXTI->PD = pending_isr; + + /* only generate soft interrupts against lines which have their IMR set */ + pending_isr &= EXTI->INTEN; + + /* iterate over all set bits */ + uint8_t pin = 0; + while (pending_isr) { + pending_isr = bitarithm_test_and_clear(pending_isr, &pin); + exti_ctx[pin].cb(exti_ctx[pin].arg); + } +} +#endif + /** @} */