mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-03 13:33:55 +00:00
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>
362 lines
8.8 KiB
C
362 lines
8.8 KiB
C
/*
|
|
* 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);
|