diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt index ca292c203cc..5cf84b88494 100644 --- a/drivers/interrupt_controller/CMakeLists.txt +++ b/drivers/interrupt_controller/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_library_sources_ifdef(CONFIG_ARCV2_INTERRUPT_UNIT intc_arcv2_irq_unit. zephyr_library_sources_ifdef(CONFIG_CAVS_ICTL intc_cavs.c) zephyr_library_sources_ifdef(CONFIG_DW_ICTL intc_dw.c) zephyr_library_sources_ifdef(CONFIG_EXTI_STM32 intc_exti_stm32.c) +zephyr_library_sources_ifdef(CONFIG_GD32_EXTI intc_gd32_exti.c) zephyr_library_sources_ifdef(CONFIG_GIC_V1 intc_gic.c) zephyr_library_sources_ifdef(CONFIG_GIC_V2 intc_gic.c) zephyr_library_sources_ifdef(CONFIG_GIC_V3 intc_gicv3.c) diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig index 706aaa2599d..bbabc355356 100644 --- a/drivers/interrupt_controller/Kconfig +++ b/drivers/interrupt_controller/Kconfig @@ -75,4 +75,6 @@ source "drivers/interrupt_controller/Kconfig.xec" source "drivers/interrupt_controller/Kconfig.eclic" +source "drivers/interrupt_controller/Kconfig.gd32_exti" + endmenu diff --git a/drivers/interrupt_controller/Kconfig.gd32_exti b/drivers/interrupt_controller/Kconfig.gd32_exti new file mode 100644 index 00000000000..71fd689d6a1 --- /dev/null +++ b/drivers/interrupt_controller/Kconfig.gd32_exti @@ -0,0 +1,12 @@ +# Copyright (c) 2021 Teslabs Engineering S.L. +# SPDX-License-Identifier: Apache-2.0 + +DT_COMPAT_GD_GD32_EXTI := gd,gd32-exti + +config GD32_EXTI + bool "GD32 Extended Interrupts and Events (EXTI) Controller" + depends on SOC_FAMILY_GD32 + default $(dt_compat_enabled,$(DT_COMPAT_GD_GD32_EXTI)) + help + Enable the GigaDevice GD32 Extended Interrupts and Events (EXTI) + controller driver. diff --git a/drivers/interrupt_controller/intc_gd32_exti.c b/drivers/interrupt_controller/intc_gd32_exti.c new file mode 100644 index 00000000000..b6d0446273c --- /dev/null +++ b/drivers/interrupt_controller/intc_gd32_exti.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2021 Teslabs Engineering S.L. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT gd_gd32_exti + +#include +#include +#include +#include +#include + +/** Unsupported line indicator */ +#define EXTI_NOTSUP 0xFFU + +/** Number of EXTI lines. */ +#define NUM_EXTI_LINES DT_INST_PROP(0, num_lines) + +/** @brief EXTI line ranges hold by a single ISR */ +struct gd32_exti_range { + /** Start of the range */ + uint8_t min; + /** End of the range */ + uint8_t max; +}; + +/** @brief EXTI line interrupt callback. */ +struct gd32_cb_data { + /** Callback function */ + gd32_exti_cb_t cb; + /** User data. */ + void *user; +}; + +/** EXTI driver data. */ +struct gd32_exti_data { + /** Array of callbacks. */ + struct gd32_cb_data cbs[NUM_EXTI_LINES]; +}; + +#ifdef CONFIG_GPIO_GD32 +static const struct gd32_exti_range line0_range = {0U, 0U}; +static const struct gd32_exti_range line1_range = {1U, 1U}; +static const struct gd32_exti_range line2_range = {2U, 2U}; +static const struct gd32_exti_range line3_range = {3U, 3U}; +static const struct gd32_exti_range line4_range = {4U, 4U}; +static const struct gd32_exti_range line5_9_range = {5U, 9U}; +static const struct gd32_exti_range line10_15_range = {10U, 15U}; +#endif /* CONFIG_GPIO_GD32 */ + +/** @brief Obtain line IRQ number if enabled. */ +#define EXTI_LINE_IRQ_COND(enabled, line) \ + COND_CODE_1(enabled, (DT_INST_IRQ_BY_NAME(0, line, irq)), (EXTI_NOTSUP)) + +static const uint8_t line2irq[NUM_EXTI_LINES] = { + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line0), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line1), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line2), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line3), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line4), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line5_9), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line5_9), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line5_9), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line5_9), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line5_9), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15), + EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15), + EXTI_NOTSUP, + EXTI_NOTSUP, + EXTI_NOTSUP, +#ifdef CONFIG_SOC_SERIES_GD32F4XX + EXTI_NOTSUP, + EXTI_NOTSUP, + EXTI_NOTSUP, + EXTI_NOTSUP, +#endif /* CONFIG_SOC_SERIES_GD32F4XX */ +}; + +static void gd32_exti_isr(void *isr_data) +{ + const struct device *dev = DEVICE_DT_INST_GET(0); + struct gd32_exti_data *data = dev->data; + const struct gd32_exti_range *range = isr_data; + + for (uint8_t i = range->min; i <= range->max; i++) { + if ((EXTI_PD & BIT(i)) != 0U) { + EXTI_PD = BIT(i); + + if (data->cbs[i].cb != NULL) { + data->cbs[i].cb(i, data->cbs[i].user); + } + } + } +} + +void gd32_exti_enable(uint8_t line) +{ + __ASSERT_NO_MSG(line < NUM_EXTI_LINES); + __ASSERT_NO_MSG(line2irq[line] != EXTI_NOTSUP); + + EXTI_INTEN |= BIT(line); + + irq_enable(line2irq[line]); +} + +void gd32_exti_disable(uint8_t line) +{ + __ASSERT_NO_MSG(line < NUM_EXTI_LINES); + __ASSERT_NO_MSG(line2irq[line] != EXTI_NOTSUP); + + EXTI_INTEN &= ~BIT(line); +} + +void gd32_exti_trigger(uint8_t line, uint8_t trigger) +{ + __ASSERT_NO_MSG(line < NUM_EXTI_LINES); + __ASSERT_NO_MSG(line2irq[line] != EXTI_NOTSUP); + + if ((trigger & GD32_EXTI_TRIG_RISING) != 0U) { + EXTI_RTEN |= BIT(line); + } else { + EXTI_RTEN &= ~BIT(line); + } + + if ((trigger & GD32_EXTI_TRIG_FALLING) != 0U) { + EXTI_FTEN |= BIT(line); + } else { + EXTI_FTEN &= ~BIT(line); + } +} + +int gd32_exti_configure(uint8_t line, gd32_exti_cb_t cb, void *user) +{ + const struct device *dev = DEVICE_DT_INST_GET(0); + struct gd32_exti_data *data = dev->data; + + __ASSERT_NO_MSG(line < NUM_EXTI_LINES); + __ASSERT_NO_MSG(line2irq[line] != EXTI_NOTSUP); + + if ((data->cbs[line].cb != NULL) && (cb != NULL)) { + return -EALREADY; + } + + data->cbs[line].cb = cb; + data->cbs[line].user = user; + + return 0; +} + +static int gd32_exti_init(const struct device *dev) +{ +#ifdef CONFIG_GPIO_GD32 + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line0, irq), + DT_INST_IRQ_BY_NAME(0, line0, priority), + gd32_exti_isr, &line0_range, 0); + + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line1, irq), + DT_INST_IRQ_BY_NAME(0, line1, priority), + gd32_exti_isr, &line1_range, 0); + + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line2, irq), + DT_INST_IRQ_BY_NAME(0, line2, priority), + gd32_exti_isr, &line2_range, 0); + + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line3, irq), + DT_INST_IRQ_BY_NAME(0, line3, priority), + gd32_exti_isr, &line3_range, 0); + + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line4, irq), + DT_INST_IRQ_BY_NAME(0, line4, priority), + gd32_exti_isr, &line4_range, 0); + + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line5_9, irq), + DT_INST_IRQ_BY_NAME(0, line5_9, priority), + gd32_exti_isr, &line5_9_range, 0); + + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line10_15, irq), + DT_INST_IRQ_BY_NAME(0, line10_15, priority), + gd32_exti_isr, &line10_15_range, 0); +#endif /* CONFIG_GPIO_GD32 */ + + return 0; +} + +static struct gd32_exti_data data; + +DEVICE_DT_INST_DEFINE(0, gd32_exti_init, NULL, &data, NULL, PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, NULL); diff --git a/include/drivers/interrupt_controller/gd32_exti.h b/include/drivers/interrupt_controller/gd32_exti.h new file mode 100644 index 00000000000..860707eff69 --- /dev/null +++ b/include/drivers/interrupt_controller/gd32_exti.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 Teslabs Engineering S.L. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_INTERRUPT_CONTROLLER_GD32_EXTI_H_ +#define ZEPHYR_INCLUDE_DRIVERS_INTERRUPT_CONTROLLER_GD32_EXTI_H_ + +#include + +#include + +/** + * @name EXTI trigger modes. + * @anchor GD32_EXTI_TRIG + * @{ + */ + +/** No trigger */ +#define GD32_EXTI_TRIG_NONE 0U +/** Trigger on rising edge */ +#define GD32_EXTI_TRIG_RISING BIT(0) +/** Trigger on falling endge */ +#define GD32_EXTI_TRIG_FALLING BIT(1) +/** Trigger on rising and falling edge */ +#define GD32_EXTI_TRIG_BOTH (GD32_EXTI_TRIG_RISING | GD32_EXTI_TRIG_FALLING) + +/** @} */ + +/** Callback for EXTI interrupt. */ +typedef void (*gd32_exti_cb_t)(uint8_t line, void *user); + +/** + * @brief Enable EXTI interrupt for the given line. + * + * @param line EXTI line. + */ +void gd32_exti_enable(uint8_t line); + +/** + * @brief Disable EXTI interrupt for the given line. + * + * @param line EXTI line. + */ +void gd32_exti_disable(uint8_t line); + +/** + * @brief Configure EXTI interrupt trigger mode for the given line. + * + * @param line EXTI line. + * @param trigger Trigger mode (see @ref GD32_EXTI_TRIG). + */ +void gd32_exti_trigger(uint8_t line, uint8_t trigger); + +/** + * @brief Configure EXTI interrupt callback. + * + * @param line EXTI line. + * @param cb Callback (NULL to disable). + * @param user User data (optional). + * + * @retval 0 On success. + * @retval -EALREADY If callback is already set and @p cb is not NULL. + */ +int gd32_exti_configure(uint8_t line, gd32_exti_cb_t cb, void *user); + +#endif /* ZEPHYR_INCLUDE_DRIVERS_INTERRUPT_CONTROLLER_GD32_EXTI_H_ */