drivers: interrupt_controller: introduce STM32 EXTI driver

add peripheral lines support
add EXTI interface

Co-authored-by: Mathieu CHOPLAIN <mathieu.choplain@st.com>
Signed-off-by: Alexander Kozhinov <ak.alexander.kozhinov@gmail.com>
This commit is contained in:
Alexander Kozhinov
2025-06-17 17:48:33 +02:00
committed by Chris Friedt
parent 5e727d7e83
commit 36ef80033c
5 changed files with 678 additions and 3 deletions

View File

@@ -7,6 +7,7 @@ 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_DW_ICTL_ACE intc_dw_ace.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_INTC_STM32 intc_gpio_stm32.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)

View File

@@ -1,16 +1,24 @@
# STM32 EXTI configuration
# Copyright (c) 2016 Open-RnD Sp. z o.o.
# Copyright (c) 2025 Alexander Kozhinov <ak.alexander.kozhinov@gmail.com>
# SPDX-License-Identifier: Apache-2.0
if SOC_FAMILY_STM32
config GPIO_INTC_STM32
bool "GPIO Interrupt/Event Controller (EXTI) Driver for STM32 family of MCUs"
config EXTI_STM32
bool "STM32 External Interrupt/Event Controller (EXTI) Driver"
default y
depends on DT_HAS_ST_STM32_EXTI_ENABLED
help
Enable GPIO interrupt controller driver for STM32 family of MCUs
Enable STM32 EXTI driver
config GPIO_INTC_STM32
bool "STM32 GPIO Interrupt/Event Controller (EXTI) Driver"
default y
depends on EXTI_STM32
help
Enable STM32 GPIO interrupt controller driver
config GPIO_INTC_STM32WB0
bool "GPIO Interrupt Controller Driver for STM32WB0 series"

View File

@@ -0,0 +1,361 @@
/*
* Copyright (c) 2016 Open-RnD Sp. z o.o.
* Copyright (c) 2017 RnDity Sp. z o.o.
* Copyright (c) 2019-23 Linaro Limited
* Copyright (C) 2025 Savoir-faire Linux, Inc.
* Copyright (c) 2025 Alexander Kozhinov <ak.alexander.kozhinov@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief STM32 External Interrupt/Event Controller (EXTI) Driver
*/
#include <soc.h>
#include <zephyr/device.h>
#include <zephyr/sys/util.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/interrupt_controller/intc_exti_stm32.h>
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
#include "stm32_hsem.h"
#include "intc_exti_stm32_priv.h"
LOG_MODULE_REGISTER(exti_stm32, CONFIG_INTC_LOG_LEVEL);
#define IS_VALID_EXTI_LINE_NUM(line_num) ((line_num) < STM32_EXTI_TOTAL_LINES_NUM)
/*
* The boilerplate for COND_CODE_x is needed because the values are not 0/1
*/
#if STM32_EXTI_TOTAL_LINES_NUM > 32
#define HAS_LINES_32_63 1
#if STM32_EXTI_TOTAL_LINES_NUM > 64
#define HAS_LINES_64_95 1
#endif /* STM32_EXTI_TOTAL_LINES_NUM > 64 */
#endif /* STM32_EXTI_TOTAL_LINES_NUM > 32 */
#define EXTI_FN_HANDLER(_fn, line_num, line) \
if (line_num < 32U) { \
_fn(0_31, line); \
IF_ENABLED(HAS_LINES_32_63, ( \
} else if (line_num < 64U) { \
_fn(32_63, line); \
)) \
IF_ENABLED(HAS_LINES_64_95, ( \
} else if (line_num < 96U) { \
_fn(64_95, line); \
)) \
} else { \
LOG_ERR("Invalid line number %u", line_num); \
__ASSERT_NO_MSG(0); \
}
#define EXTI_FN_RET_HANDLER(_fn, ret, line_num, line) \
if (line_num < 32U) { \
*ret = _fn(0_31, line); \
IF_ENABLED(HAS_LINES_32_63, ( \
} else if (line_num < 64U) { \
*ret = _fn(32_63, line); \
)) \
IF_ENABLED(HAS_LINES_64_95, ( \
} else if (line_num < 96U) { \
*ret = _fn(64_95, line); \
)) \
} else { \
LOG_ERR("Invalid line number %u", line_num); \
__ASSERT_NO_MSG(0); \
}
bool stm32_exti_is_pending(uint32_t line_num)
{
bool ret = false;
const uint32_t line = exti_linenum_to_ll_exti_line(line_num);
if (!IS_VALID_EXTI_LINE_NUM(line_num)) {
LOG_ERR("Invalid line number %u", line_num);
return false;
}
z_stm32_hsem_lock(CFG_HW_EXTI_SEMID, HSEM_LOCK_DEFAULT_RETRY);
/*
* Note: we can't use EXTI_FN_HANDLER here because we care
* about the return value of EXTI_IS_ACTIVE_FLAG.
*/
EXTI_FN_RET_HANDLER(EXTI_IS_ACTIVE_FLAG, &ret, line_num, line);
z_stm32_hsem_unlock(CFG_HW_EXTI_SEMID);
return ret;
}
int stm32_exti_clear_pending(uint32_t line_num)
{
const uint32_t line = exti_linenum_to_ll_exti_line(line_num);
if (!IS_VALID_EXTI_LINE_NUM(line_num)) {
LOG_ERR("Invalid line number %u", line_num);
return -EINVAL;
}
z_stm32_hsem_lock(CFG_HW_EXTI_SEMID, HSEM_LOCK_DEFAULT_RETRY);
EXTI_FN_HANDLER(EXTI_CLEAR_FLAG, line_num, line);
z_stm32_hsem_unlock(CFG_HW_EXTI_SEMID);
return 0;
}
int stm32_exti_sw_interrupt(uint32_t line_num)
{
const uint32_t line = exti_linenum_to_ll_exti_line(line_num);
if (!IS_VALID_EXTI_LINE_NUM(line_num)) {
LOG_ERR("Invalid line number %u", line_num);
return -EINVAL;
}
z_stm32_hsem_lock(CFG_HW_EXTI_SEMID, HSEM_LOCK_DEFAULT_RETRY);
EXTI_FN_HANDLER(EXTI_GENERATE_SWI, line_num, line);
z_stm32_hsem_unlock(CFG_HW_EXTI_SEMID);
return 0;
}
/** Enables the peripheral clock required to access EXTI registers */
static int stm32_exti_enable_clocks(void)
{
/* Initialize to 0 for series where there is nothing to do. */
int ret = 0;
#if DT_NODE_HAS_PROP(EXTI_NODE, clocks)
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
if (!device_is_ready(clk)) {
LOG_ERR("Clock control device not ready");
return -ENODEV;
}
const struct stm32_pclken pclken = {
.bus = DT_CLOCKS_CELL(EXTI_NODE, bus),
.enr = DT_CLOCKS_CELL(EXTI_NODE, bits)
};
ret = clock_control_on(clk, (clock_control_subsys_t) &pclken);
#endif
return ret;
}
/**
* @brief Initializes the EXTI interrupt controller driver
*/
static int stm32_exti_init(const struct device *dev)
{
ARG_UNUSED(dev);
return stm32_exti_enable_clocks();
}
/**
* @brief Enable EXTI interrupts.
*
* @param line_num EXTI line number
* @param line LL EXTI line
*/
static void stm32_exti_enable_it(uint32_t line_num, uint32_t line)
{
EXTI_FN_HANDLER(EXTI_ENABLE_IT, line_num, line);
}
/**
* @brief Disable EXTI interrupts.
*
* @param line_num EXTI line number
* @param line LL EXTI line
*/
static void stm32_exti_disable_it(uint32_t line_num, uint32_t line)
{
EXTI_FN_HANDLER(EXTI_DISABLE_IT, line_num, line);
}
/**
* @brief Enables rising trigger for specified EXTI line
*
* @param line_num EXTI line number
* @param line LL EXTI line
*/
static void stm32_exti_enable_rising_trig(uint32_t line_num, uint32_t line)
{
EXTI_FN_HANDLER(EXTI_ENABLE_RISING_TRIG, line_num, line);
}
/**
* @brief Disables rising trigger for specified EXTI line
*
* @param line_num EXTI line number
* @param line LL EXTI line
*/
static void stm32_exti_disable_rising_trig(uint32_t line_num, uint32_t line)
{
EXTI_FN_HANDLER(EXTI_DISABLE_RISING_TRIG, line_num, line);
}
/**
* @brief Enables falling trigger for specified EXTI line
*
* @param line_num EXTI line number
* @param line LL EXTI line
*/
static void stm32_exti_enable_falling_trig(uint32_t line_num, uint32_t line)
{
EXTI_FN_HANDLER(EXTI_ENABLE_FALLING_TRIG, line_num, line);
}
/**
* @brief Disables falling trigger for specified EXTI line
*
* @param line_num EXTI line number
* @param line LL EXTI line
*/
static void stm32_exti_disable_falling_trig(uint32_t line_num, uint32_t line)
{
EXTI_FN_HANDLER(EXTI_DISABLE_FALLING_TRIG, line_num, line);
}
/**
* @brief Selects EXTI trigger mode
*
* @param line_num EXTI line number
* @param line LL EXTI line
* @param mode EXTI mode
*/
static void stm32_exti_select_line_trigger(uint32_t line_num, uint32_t line,
uint32_t trg)
{
switch (trg) {
case STM32_EXTI_TRIG_NONE:
stm32_exti_disable_rising_trig(line_num, line);
stm32_exti_disable_falling_trig(line_num, line);
break;
case STM32_EXTI_TRIG_RISING:
stm32_exti_enable_rising_trig(line_num, line);
stm32_exti_disable_falling_trig(line_num, line);
break;
case STM32_EXTI_TRIG_FALLING:
stm32_exti_enable_falling_trig(line_num, line);
stm32_exti_disable_rising_trig(line_num, line);
break;
case STM32_EXTI_TRIG_BOTH:
stm32_exti_enable_rising_trig(line_num, line);
stm32_exti_enable_falling_trig(line_num, line);
break;
default:
LOG_ERR("Unsupported EXTI trigger 0x%X", trg);
break;
}
}
/**
* @brief Enable EXTI event.
*
* @param line_num EXTI line number
* @param line LL EXTI line
*/
static void stm32_exti_enable_event(uint32_t line_num, uint32_t line)
{
EXTI_FN_HANDLER(EXTI_ENABLE_EVENT, line_num, line);
}
/**
* @brief Disable EXTI interrupts.
*
* @param line_num EXTI line number
* @param line LL EXTI line
*/
static void stm32_exti_disable_event(uint32_t line_num, uint32_t line)
{
EXTI_FN_HANDLER(EXTI_DISABLE_EVENT, line_num, line);
}
/**
* @brief Enables external interrupt/event for specified EXTI line
*
* @param line_num EXTI line number
* @param line LL EXTI line
* @param mode EXTI mode
*/
static void stm32_exti_set_mode(uint32_t line_num, uint32_t line,
stm32_exti_mode mode)
{
switch (mode) {
case STM32_EXTI_MODE_NONE:
stm32_exti_disable_event(line_num, line);
stm32_exti_disable_it(line_num, line);
break;
case STM32_EXTI_MODE_IT:
stm32_exti_disable_event(line_num, line);
stm32_exti_enable_it(line_num, line);
break;
case STM32_EXTI_MODE_EVENT:
stm32_exti_disable_it(line_num, line);
stm32_exti_enable_event(line_num, line);
break;
case STM32_EXTI_MODE_BOTH:
stm32_exti_enable_it(line_num, line);
stm32_exti_enable_event(line_num, line);
break;
default:
LOG_ERR("Unsupported EXTI mode %u", mode);
break;
}
}
int stm32_exti_enable(uint32_t line_num, stm32_exti_trigger_type trigger,
stm32_exti_mode mode)
{
const uint32_t line = exti_linenum_to_ll_exti_line(line_num);
if (!IS_VALID_EXTI_LINE_NUM(line_num)) {
LOG_ERR("Invalid line number %u", line_num);
return -EINVAL;
}
z_stm32_hsem_lock(CFG_HW_EXTI_SEMID, HSEM_LOCK_DEFAULT_RETRY);
stm32_exti_select_line_trigger(line_num, line, trigger);
stm32_exti_set_mode(line_num, line, mode);
z_stm32_hsem_unlock(CFG_HW_EXTI_SEMID);
return 0;
}
int stm32_exti_disable(uint32_t line_num)
{
const uint32_t line = exti_linenum_to_ll_exti_line(line_num);
if (!IS_VALID_EXTI_LINE_NUM(line_num)) {
LOG_ERR("Invalid line number %u", line_num);
return -EINVAL;
}
z_stm32_hsem_lock(CFG_HW_EXTI_SEMID, HSEM_LOCK_DEFAULT_RETRY);
stm32_exti_set_mode(line_num, line, STM32_EXTI_MODE_NONE);
stm32_exti_select_line_trigger(line_num, line, STM32_EXTI_TRIG_NONE);
z_stm32_hsem_unlock(CFG_HW_EXTI_SEMID);
return 0;
}
DEVICE_DT_DEFINE(EXTI_NODE, &stm32_exti_init,
NULL, NULL, NULL,
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY,
NULL);

View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) 2025 Alexander Kozhinov <ak.alexander.kozhinov@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_EXTI_STM32_PRIV_H_
#define ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_EXTI_STM32_PRIV_H_
#include <zephyr/device.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>
#include <zephyr/drivers/interrupt_controller/intc_exti_stm32.h>
#include <stm32_ll_bus.h>
#include <stm32_ll_exti.h>
#include <stm32_ll_system.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* NOTE: This implementation currently does not support configurations where a
* single CPU has access to multiple EXTI instances. Supporting multiple EXTI
* instances per CPU (such as possible on STM32MP2 series with both M33 and M0+
* cores) will require changes to this macro and potentially other parts of the
* driver.
*/
#define EXTI_NODE DT_INST(0, st_stm32_exti)
/**
* STM32MP1 has up to 96 EXTI lines (RM0475, Table 112. STM32MP13xx EXTI Events),
* but some ranges contain only direct lines, so the LL functions that are
* only valid for configurable lines are not provided for these ranges.
* However, we are relying on all these functions being present as part of our
* utility macros. Define dummy stubs for these functions to allow the main driver
* to work properly on this special series.
*
* NOTE: These stubs are only for compatibility and will assert if called for
* unsupported lines. Always ensure the correct line range is used.
*/
#define STM32_EXTI_TOTAL_LINES_NUM DT_PROP(EXTI_NODE, num_lines)
/**
* @brief Define CPU number suffix for STM32 series.
*/
#if (defined(CONFIG_SOC_SERIES_STM32H7X) && defined(CONFIG_CPU_CORTEX_M4)) || \
(defined(CONFIG_SOC_SERIES_STM32MP2X) && defined(CONFIG_CPU_CORTEX_M33))
#define CPU_NR _C2
#elif defined(CONFIG_SOC_SERIES_STM32MP2X) && defined(CONFIG_CPU_CORTEX_M0)
#define CPU_NR _C3
#else
/* NOTE: usually one CPU (e.g. C1) only is omitted and leaved empty */
#define CPU_NR
#endif
/**
* @brief Define EXTI_LL_INST for STM32MP2X series, since it may have multiple instances
* For all other than STM32MP2X series the EXTI instance is defined within LL driver
* itself s.t. EXTI_LL_INST will be empty.
*/
#if defined(CONFIG_SOC_SERIES_STM32MP2X)
#define EXTI_LL_INST ((EXTI_TypeDef *)DT_REG_ADDR(EXTI_NODE)),
#else /* CONFIG_SOC_SERIES_STM32MP2X */
#define EXTI_LL_INST
#endif /* CONFIG_SOC_SERIES_STM32MP2X */
#define EXTI_LINE_NOT_SUPP_ASSERT(line) \
{ \
LOG_ERR("Unsupported line number %u", line); \
__ASSERT_NO_MSG(0); \
}
#define EXTI_LINE_NOP(line) \
{ \
ARG_UNUSED(line); \
}
#if defined(CONFIG_SOC_SERIES_STM32MP1X) || defined(CONFIG_SOC_SERIES_STM32MP13X)
/*
* NOTE: There is currently no other option than to define LL_ prefixed functions
* that are missing in the original drivers as stubs for unsupported EXTI lines.
* This is required for compatibility across all STM32 series, until a more
* flexible solution is available.
*/
#define LL_EXTI_IsActiveRisingFlag_32_63(line) 0
#define LL_EXTI_IsActiveFallingFlag_32_63(line) 0
#define LL_EXTI_ClearRisingFlag_32_63(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_ClearFallingFlag_32_63(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_GenerateSWI_32_63(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_EnableRisingTrig_32_63(line) EXTI_LINE_NOP(line)
#define LL_EXTI_EnableFallingTrig_32_63(line) EXTI_LINE_NOP(line)
#define LL_EXTI_DisableRisingTrig_32_63(line) EXTI_LINE_NOP(line)
#define LL_EXTI_DisableFallingTrig_32_63(line) EXTI_LINE_NOP(line)
#define LL_EXTI_EnableEvent_32_63(line) EXTI_LINE_NOP(line)
#define LL_EXTI_DisableEvent_32_63(line) EXTI_LINE_NOP(line)
#if defined(CONFIG_SOC_SERIES_STM32MP13X)
#define LL_EXTI_IsActiveRisingFlag_64_95(line) 0
#define LL_EXTI_IsActiveFallingFlag_64_95(line) 0
#define LL_EXTI_ClearRisingFlag_64_95(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_ClearFallingFlag_64_95(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_GenerateSWI_64_95(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_EnableRisingTrig_64_95(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_DisableRisingTrig_64_95(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_EnableFallingTrig_64_95(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_DisableFallingTrig_64_95(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_EnableEvent_64_95(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#define LL_EXTI_DisableEvent_64_95(line) EXTI_LINE_NOT_SUPP_ASSERT(line)
#endif /* !CONFIG_SOC_SERIES_STM32MP13X */
#endif /* CONFIG_SOC_SERIES_STM32MP1X || CONFIG_SOC_SERIES_STM32MP13X */
/* Define general macros line-range independent */
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32g0_exti) || defined(CONFIG_SOC_SERIES_STM32MP2X)
#if defined(CONFIG_SOC_SERIES_STM32MP2X)
#define EXTI_IS_ACTIVE_RISING_FLAG(line_range, line) \
CONCAT(LL, _EXTI_IsActiveRisingFlag_##line_range)(EXTI_LL_INST line)
#define EXTI_IS_ACTIVE_FALLING_FLAG(line_range, line) \
CONCAT(LL, _EXTI_IsActiveFallingFlag_##line_range)(EXTI_LL_INST line)
#define EXTI_CLEAR_RISING_FLAG(line_range, line) \
CONCAT(LL, _EXTI_ClearRisingFlag_##line_range)(EXTI_LL_INST line)
#define EXTI_CLEAR_FALLING_FLAG(line_range, line) \
CONCAT(LL, _EXTI_ClearFallingFlag_##line_range)(EXTI_LL_INST line)
#else /* !CONFIG_SOC_SERIES_STM32MP2X */
#define EXTI_IS_ACTIVE_RISING_FLAG(line_range, line) \
CONCAT(LL, CPU_NR, _EXTI_IsActiveRisingFlag_##line_range)(EXTI_LL_INST line)
#define EXTI_IS_ACTIVE_FALLING_FLAG(line_range, line) \
CONCAT(LL, CPU_NR, _EXTI_IsActiveFallingFlag_##line_range)(EXTI_LL_INST line)
#define EXTI_CLEAR_RISING_FLAG(line_range, line) \
CONCAT(LL, CPU_NR, _EXTI_ClearRisingFlag_##line_range)(EXTI_LL_INST line)
#define EXTI_CLEAR_FALLING_FLAG(line_range, line) \
CONCAT(LL, CPU_NR, _EXTI_ClearFallingFlag_##line_range)(EXTI_LL_INST line)
#endif /* CONFIG_SOC_SERIES_STM32MP2X */
#define EXTI_CLEAR_FLAG(line_range, line) \
{ \
EXTI_CLEAR_RISING_FLAG(line_range, (line)); \
EXTI_CLEAR_FALLING_FLAG(line_range, (line)); \
}
#define EXTI_IS_ACTIVE_FLAG(line_range, line) \
(EXTI_IS_ACTIVE_RISING_FLAG(line_range, (line)) || \
EXTI_IS_ACTIVE_FALLING_FLAG(line_range, (line)))
#else /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32g0_exti) */
#define EXTI_CLEAR_FLAG(line_range, line) \
CONCAT(LL, CPU_NR, _EXTI_ClearFlag_##line_range)(line)
#define EXTI_IS_ACTIVE_FLAG(line_range, line) \
CONCAT(LL, CPU_NR, _EXTI_IsActiveFlag_##line_range)(line)
#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32g0_exti) */
/* Define general macros line-range independent */
#define EXTI_ENABLE_IT(line_range, line) \
CONCAT(LL, CPU_NR, _EXTI_EnableIT_##line_range)(EXTI_LL_INST line)
#define EXTI_DISABLE_IT(line_range, line) \
CONCAT(LL, CPU_NR, _EXTI_DisableIT_##line_range)(EXTI_LL_INST line)
#define EXTI_ENABLE_EVENT(line_range, line) \
CONCAT(LL, CPU_NR, _EXTI_EnableEvent_##line_range)(EXTI_LL_INST line)
#define EXTI_DISABLE_EVENT(line_range, line) \
CONCAT(LL, CPU_NR, _EXTI_DisableEvent_##line_range)(EXTI_LL_INST line)
#define EXTI_ENABLE_RISING_TRIG(line_range, line) \
CONCAT(LL, _EXTI_EnableRisingTrig_##line_range)(EXTI_LL_INST line)
#define EXTI_ENABLE_FALLING_TRIG(line_range, line) \
CONCAT(LL, _EXTI_EnableFallingTrig_##line_range)(EXTI_LL_INST line)
#define EXTI_DISABLE_FALLING_TRIG(line_range, line) \
CONCAT(LL, _EXTI_DisableFallingTrig_##line_range)(EXTI_LL_INST line)
#define EXTI_DISABLE_RISING_TRIG(line_range, line) \
CONCAT(LL, _EXTI_DisableRisingTrig_##line_range)(EXTI_LL_INST line)
#define EXTI_GENERATE_SWI(line_range, line) \
CONCAT(LL, _EXTI_GenerateSWI_##line_range)(EXTI_LL_INST line)
/**
* @returns LL_EXTI_LINE_n define corresponding to EXTI line number
*/
static inline uint32_t exti_linenum_to_ll_exti_line(uint32_t line_num)
{
return BIT(line_num % 32);
}
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_EXTI_STM32_PRIV_H_ */

View File

@@ -0,0 +1,101 @@
/*
* Copyright (c) 2025 Alexander Kozhinov <ak.alexander.kozhinov@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief STM32 EXTI interrupt controller API
*
* This API is used to interact with STM32 EXTI interrupt controller
*/
#ifndef ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_EXTI_STM32_H_
#define ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_EXTI_STM32_H_
#include <zephyr/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief EXTI interrupt trigger type
*/
typedef enum {
/* No trigger */
STM32_EXTI_TRIG_NONE = 0x0,
/* Trigger on rising edge */
STM32_EXTI_TRIG_RISING = 0x1,
/* Trigger on falling edge */
STM32_EXTI_TRIG_FALLING = 0x2,
/* Trigger on both rising and falling edge */
STM32_EXTI_TRIG_BOTH = 0x3,
} stm32_exti_trigger_type;
/**
* @brief EXTI line mode
*/
typedef enum {
/* Generate interrupts only */
STM32_EXTI_MODE_IT = 0x0,
/* Generate events only */
STM32_EXTI_MODE_EVENT = 0x1,
/* Generate interrupts and events */
STM32_EXTI_MODE_BOTH = 0x2,
/* Disable interrupts and events */
STM32_EXTI_MODE_NONE = 0x3,
} stm32_exti_mode;
/**
* @brief Enable EXTI line.
* @note The caller driver is responsible for registering an ISR and
* enabling the corresponding interrupt if MODE_IT is selected.
*
* @param line_num EXTI line number
* @param trigger EXTI trigger type (see @ref stm32_exti_trigger_type)
* @param mode EXTI mode (see @ref stm32_exti_mode)
* @returns 0 on success, -EINVAL if @p line_num is invalid
*/
int stm32_exti_enable(uint32_t line_num, stm32_exti_trigger_type trigger,
stm32_exti_mode mode);
/**
* @brief Disable EXTI line.
* After this function has been called, EXTI line @p line_num will
* not generate further interrupts or events.
*
* @param line_num EXTI line number
* @returns 0 on success, -EINVAL if @p line is invalid
*/
int stm32_exti_disable(uint32_t line_num);
/**
* @brief Checks interrupt pending bit for specified EXTI line
*
* @param line_num EXTI line number
* @returns true if @p line is pending, false otherwise
*/
bool stm32_exti_is_pending(uint32_t line_num);
/**
* @brief Clears interrupt pending bit for specified EXTI line
*
* @param line_num EXTI line number
* @returns 0 on success, -EINVAL if @p line is invalid
*/
int stm32_exti_clear_pending(uint32_t line_num);
/**
* @brief Generates SW interrupt for specified EXTI line number
*
* @param line_num EXTI line number
* @returns 0 on success, -EINVAL if @p line is invalid
*/
int stm32_exti_sw_interrupt(uint32_t line_num);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_EXTI_STM32_H_ */