Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpu/gd32v: add periph_gpio_irq support #19185

Merged
merged 1 commit into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cpu/gd32v/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions cpu/gd32v/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions cpu/gd32v/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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 */

/**
Expand Down
137 changes: 135 additions & 2 deletions cpu/gd32v/periph/gpio.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2020 Koen Zandberg <[email protected]>
* Copyright (C) 2014-2015 Freie Universität Berlin
* 2020 Koen Zandberg <[email protected]>
* 2023 Gunar Schorcht <[email protected]>
*
* 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
Expand All @@ -10,13 +12,19 @@
* @{
*
* @file
* @brief GD32V GPIO implementation
* @brief Low-level GPIO driver implementation for GD32V
*
* @author Koen Zandberg <[email protected]>
* @author Hauke Petersen <[email protected]>
* @author Thomas Eichinger <[email protected]>
* @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"

Expand All @@ -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
*/
Expand Down Expand Up @@ -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<n> shares
* the EXTI line with PB<n>, PC<n>, PD<n> and PE<n>. */
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

/** @} */