/*
* Copyright (c) 2016 Open-RnD Sp. z o.o.
* Copyright (c) 2017 RnDity Sp. z o.o.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief Driver for External interrupt/event controller in STM32 MCUs
*
* Based on reference manuals:
* RM0008 Reference Manual: STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx
* and STM32F107xx advanced ARM-based 32-bit MCUs
* and
* RM0368 Reference manual STM32F401xB/C and STM32F401xD/E
* advanced ARM-based 32-bit MCUs
*
* Chapter 10.2: External interrupt/event controller (EXTI)
*
*/
#include <device.h>
#include <soc.h>
#include <misc/__assert.h>
#include "exti_stm32.h"
#ifdef CONFIG_SOC_SERIES_STM32F0X
#define EXTI_LINES 32
#elif CONFIG_SOC_SERIES_STM32F1X
#define EXTI_LINES 19
#elif CONFIG_SOC_STM32F303XC
#define EXTI_LINES 36
#elif CONFIG_SOC_STM32F334X8
#define EXTI_LINES 36
#elif CONFIG_SOC_STM32F373XC
#define EXTI_LINES 29
#elif CONFIG_SOC_SERIES_STM32F4X
#define EXTI_LINES 23
#elif defined(CONFIG_SOC_SERIES_STM32L0X)
#define EXTI_LINES 30
#elif defined(CONFIG_SOC_SERIES_STM32L4X)
#define EXTI_LINES 40
#endif
/* 10.3.7 EXTI register map */
struct stm32_exti {
/* EXTI_IMR */
u32_t imr;
/* EXTI_EMR */
u32_t emr;
/* EXTI_RTSR */
u32_t rtsr;
/* EXTI_FTSR */
u32_t ftsr;
/* EXTI_SWIER */
u32_t swier;
/* EXTI_PR */
u32_t pr;
};
/* wrapper for user callback */
struct __exti_cb {
stm32_exti_callback_t cb;
void *data;
};
/* driver data */
struct stm32_exti_data {
/* per-line callbacks */
struct __exti_cb cb[EXTI_LINES];
};
/*
* return the proper base addr based on the line number, also we adjust
* the line number to be relative to the base, we do this here to save
* a bit of code size
*/
static inline struct stm32_exti *get_exti_addr_adjust_line(int *line)
{
struct stm32_exti *base = (struct stm32_exti *)EXTI_BASE;
#if EXTI_LINES > 32
if (*line > 31) {
*line -= 32;
return base + 1;
}
#endif
return base;
}
void stm32_exti_enable(int line)
{
volatile struct stm32_exti *exti = get_exti_addr_adjust_line(&line);
int irqnum = 0;
exti->imr |= 1 << line;
#ifdef CONFIG_SOC_SERIES_STM32F0X
if (line >= 4 && line <= 15) {
irqnum = STM32F0_IRQ_EXTI4_15;
} else if (line >= 2 && line <= 3) {
irqnum = STM32F0_IRQ_EXTI2_3;
} else if (line >= 0 && line <= 1) {
irqnum = STM32F0_IRQ_EXTI0_1;
} else {
/* > 15 are not mapped on an IRQ */
/*
* On STM32F0X, this function also support enabling EXTI
* lines that are not connected to an IRQ. This might be used
* by other drivers or boards, to allow the device wakeup on
* some non-GPIO signals.
*/
return;
}
#elif CONFIG_SOC_SERIES_STM32F1X
if (line >= 5 && line <= 9) {
irqnum = STM32F1_IRQ_EXTI9_5;
} else if (line >= 10 && line <= 15) {
irqnum = STM32F1_IRQ_EXTI15_10;
} else {
/* pins 0..4 are mapped to EXTI0.. EXTI4 */
irqnum = STM32F1_IRQ_EXTI0 + line;
}
#elif CONFIG_SOC_SERIES_STM32F3X
if (line >= 5 && line <= 9) {
irqnum = STM32F3_IRQ_EXTI9_5;
} else if (line >= 10 && line <= 15) {
irqnum = STM32F3_IRQ_EXTI15_10;
} else {
/* pins 0..4 are mapped to EXTI0.. EXTI4 */
irqnum = STM32F3_IRQ_EXTI0 + line;
}
#elif CONFIG_SOC_SERIES_STM32F4X
if (line >= 5 && line <= 9) {
irqnum = STM32F4_IRQ_EXTI9_5;
} else if (line >= 10 && line <= 15) {
irqnum = STM32F4_IRQ_EXTI15_10;
} else if (line >= 0 && line <= 4) {
/* pins 0..4 are mapped to EXTI0.. EXTI4 */
irqnum = STM32F4_IRQ_EXTI0 + line;
} else {
switch (line) {
case 16:
irqnum = STM32F4_IRQ_EXTI16;
break;
case 17:
irqnum = STM32F4_IRQ_EXTI17;
break;
case 18:
irqnum = STM32F4_IRQ_EXTI18;
break;
case 21:
irqnum = STM32F4_IRQ_EXTI21;
break;
case 22:
irqnum = STM32F4_IRQ_EXTI22;
break;
}
}
#elif defined(CONFIG_SOC_SERIES_STM32L0X)
if (line >= 4 && line <= 15) {
irqnum = STM32L0_IRQ_EXTI4_15;
} else if (line >= 2 && line <= 3) {
irqnum = STM32L0_IRQ_EXTI2_3;
} else if (line >= 0 && line <= 1) {
irqnum = STM32L0_IRQ_EXTI0_1;
} else {
/* > 15 are not mapped on an IRQ */
/*
* On STM32L0X, this function also support enabling EXTI
* lines that are not connected to an IRQ. This might be used
* by other drivers or boards, to allow the device wakeup on
* some non-GPIO signals.
*/
return;
}
#elif defined(CONFIG_SOC_SERIES_STM32L4X)
if (line >= 5 && line <= 9) {
irqnum = STM32L4_IRQ_EXTI9_5;
} else if (line >= 10 && line <= 15) {
irqnum = STM32L4_IRQ_EXTI15_10;
} else if (line < 5) {
/* pins 0..4 are mapped to EXTI0.. EXTI4 */
irqnum = STM32L4_IRQ_EXTI0 + line;
} else {/* > 15 are not mapped on an IRQ */
/*
* On STM32L4X, this function also support enabling EXTI
* lines that are not connected to an IRQ. This might be used
* by other drivers or boards, to allow the device wakeup on
* some non-GPIO signals.
*/
return;
}
#else
#error "Unknown STM32 SoC"
#endif
irq_enable(irqnum);
}
void stm32_exti_disable(int line)
{
volatile struct stm32_exti *exti = get_exti_addr_adjust_line(&line);
exti->imr &= ~(1 << line);
}
/**
* @brief check if interrupt is pending
*
* @param line line number
*/
static inline int stm32_exti_is_pending(int line)
{
volatile struct stm32_exti *exti = get_exti_addr_adjust_line(&line);
return (exti->pr & (1 << line)) ? 1 : 0;
}
/**
* @brief clear pending interrupt bit
*
* @param line line number
*/
static inline void stm32_exti_clear_pending(int line)
{
volatile struct stm32_exti *exti = get_exti_addr_adjust_line(&line);
exti->pr = 1 << line;
}
void stm32_exti_trigger(int line, int trigger)
{
volatile struct stm32_exti *exti = get_exti_addr_adjust_line(&line);
if (trigger & STM32_EXTI_TRIG_RISING) {
exti->rtsr |= 1 << line;
}
if (trigger & STM32_EXTI_TRIG_FALLING) {
exti->ftsr |= 1 << line;
}
}
/**
* @brief EXTI ISR handler
*
* Check EXTI lines in range @min @max for pending interrupts
*
* @param arg isr argument
* @param min low end of EXTI# range
* @param max low end of EXTI# range
*/
static void __stm32_exti_isr(int min, int max, void *arg)
{
struct device *dev = arg;
struct stm32_exti_data *data = dev->driver_data;
int line;
/* see which bits are set */
for (line = min; line < max; line++) {
/* check if interrupt is pending */
if (stm32_exti_is_pending(line)) {
/* clear pending interrupt */
stm32_exti_clear_pending(line);
/* run callback only if one is registered */
if (!data->cb[line].cb) {
continue;
}
data->cb[line].cb(line, data->cb[line].data);
}
}
}
#if defined(CONFIG_SOC_SERIES_STM32F0X) || defined(CONFIG_SOC_SERIES_STM32L0X)
static inline void __stm32_exti_isr_0_1(void *arg)
{
__stm32_exti_isr(0, 2, arg);
}
static inline void __stm32_exti_isr_2_3(void *arg)
{
__stm32_exti_isr(2, 4, arg);
}
static inline void __stm32_exti_isr_4_15(void *arg)
{
__stm32_exti_isr(4, 16, arg);
}
#else
static inline void __stm32_exti_isr_0(void *arg)
{
__stm32_exti_isr(0, 1, arg);
}
static inline void __stm32_exti_isr_1(void *arg)
{
__stm32_exti_isr(1, 2, arg);
}
static inline void __stm32_exti_isr_2(void *arg)
{
__stm32_exti_isr(2, 3, arg);
}
static inline void __stm32_exti_isr_3(void *arg)
{
__stm32_exti_isr(3, 4, arg);
}
static inline void __stm32_exti_isr_4(void *arg)
{
__stm32_exti_isr(4, 5, arg);
}
static inline void __stm32_exti_isr_9_5(void *arg)
{
__stm32_exti_isr(5, 10, arg);
}
static inline void __stm32_exti_isr_15_10(void *arg)
{
__stm32_exti_isr(10, 16, arg);
}
#ifdef CONFIG_SOC_SERIES_STM32F4X
static inline void __stm32_exti_isr_16(void *arg)
{
__stm32_exti_isr(16, 17, arg);
}
static inline void __stm32_exti_isr_17(void *arg)
{
__stm32_exti_isr(17, 18, arg);
}
static inline void __stm32_exti_isr_18(void *arg)
{
__stm32_exti_isr(18, 19, arg);
}
static inline void __stm32_exti_isr_21(void *arg)
{
__stm32_exti_isr(21, 22, arg);
}
static inline void __stm32_exti_isr_22(void *arg)
{
__stm32_exti_isr(22, 23, arg);
}
#endif /* CONFIG_SOC_SERIES_STM32F4X */
#endif /* CONFIG_SOC_SERIES_STM32F0X */
static void __stm32_exti_connect_irqs(struct device *dev);
/**
* @brief initialize EXTI device driver
*/
static int stm32_exti_init(struct device *dev)
{
__stm32_exti_connect_irqs(dev);
return 0;
}
static struct stm32_exti_data exti_data;
DEVICE_INIT(exti_stm32, STM32_EXTI_NAME, stm32_exti_init,
&exti_data, NULL,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
/**
* @brief set & unset for the interrupt callbacks
*/
void stm32_exti_set_callback(int line, stm32_exti_callback_t cb, void *arg)
{
struct device *dev = DEVICE_GET(exti_stm32);
struct stm32_exti_data *data = dev->driver_data;
__ASSERT(data->cb[line].cb == NULL,
"EXTI %d callback already registered", line);
data->cb[line].cb = cb;
data->cb[line].data = arg;
}
void stm32_exti_unset_callback(int line)
{
struct device *dev = DEVICE_GET(exti_stm32);
struct stm32_exti_data *data = dev->driver_data;
data->cb[line].cb = NULL;
data->cb[line].data = NULL;
}
/**
* @brief connect all interrupts
*/
static void __stm32_exti_connect_irqs(struct device *dev)
{
ARG_UNUSED(dev);
#ifdef CONFIG_SOC_SERIES_STM32F0X
IRQ_CONNECT(STM32F0_IRQ_EXTI0_1,
CONFIG_EXTI_STM32_EXTI1_0_IRQ_PRI,
__stm32_exti_isr_0_1, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F0_IRQ_EXTI2_3,
CONFIG_EXTI_STM32_EXTI3_2_IRQ_PRI,
__stm32_exti_isr_2_3, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F0_IRQ_EXTI4_15,
CONFIG_EXTI_STM32_EXTI15_4_IRQ_PRI,
__stm32_exti_isr_4_15, DEVICE_GET(exti_stm32),
0);
#elif CONFIG_SOC_SERIES_STM32F1X
IRQ_CONNECT(STM32F1_IRQ_EXTI0,
CONFIG_EXTI_STM32_EXTI0_IRQ_PRI,
__stm32_exti_isr_0, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F1_IRQ_EXTI1,
CONFIG_EXTI_STM32_EXTI1_IRQ_PRI,
__stm32_exti_isr_1, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F1_IRQ_EXTI2,
CONFIG_EXTI_STM32_EXTI2_IRQ_PRI,
__stm32_exti_isr_2, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F1_IRQ_EXTI3,
CONFIG_EXTI_STM32_EXTI3_IRQ_PRI,
__stm32_exti_isr_3, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F1_IRQ_EXTI4,
CONFIG_EXTI_STM32_EXTI4_IRQ_PRI,
__stm32_exti_isr_4, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F1_IRQ_EXTI9_5,
CONFIG_EXTI_STM32_EXTI9_5_IRQ_PRI,
__stm32_exti_isr_9_5, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F1_IRQ_EXTI15_10,
CONFIG_EXTI_STM32_EXTI15_10_IRQ_PRI,
__stm32_exti_isr_15_10, DEVICE_GET(exti_stm32),
0);
#elif CONFIG_SOC_SERIES_STM32F3X
IRQ_CONNECT(STM32F3_IRQ_EXTI0,
CONFIG_EXTI_STM32_EXTI0_IRQ_PRI,
__stm32_exti_isr_0, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F3_IRQ_EXTI1,
CONFIG_EXTI_STM32_EXTI1_IRQ_PRI,
__stm32_exti_isr_1, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F3_IRQ_EXTI2_TS,
CONFIG_EXTI_STM32_EXTI2_IRQ_PRI,
__stm32_exti_isr_2, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F3_IRQ_EXTI3,
CONFIG_EXTI_STM32_EXTI3_IRQ_PRI,
__stm32_exti_isr_3, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F3_IRQ_EXTI4,
CONFIG_EXTI_STM32_EXTI4_IRQ_PRI,
__stm32_exti_isr_4, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F3_IRQ_EXTI9_5,
CONFIG_EXTI_STM32_EXTI9_5_IRQ_PRI,
__stm32_exti_isr_9_5, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F3_IRQ_EXTI15_10,
CONFIG_EXTI_STM32_EXTI15_10_IRQ_PRI,
__stm32_exti_isr_15_10, DEVICE_GET(exti_stm32),
0);
#elif CONFIG_SOC_SERIES_STM32F4X
IRQ_CONNECT(STM32F4_IRQ_EXTI0,
CONFIG_EXTI_STM32_EXTI0_IRQ_PRI,
__stm32_exti_isr_0, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI1,
CONFIG_EXTI_STM32_EXTI1_IRQ_PRI,
__stm32_exti_isr_1, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI2,
CONFIG_EXTI_STM32_EXTI2_IRQ_PRI,
__stm32_exti_isr_2, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI3,
CONFIG_EXTI_STM32_EXTI3_IRQ_PRI,
__stm32_exti_isr_3, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI4,
CONFIG_EXTI_STM32_EXTI4_IRQ_PRI,
__stm32_exti_isr_4, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI9_5,
CONFIG_EXTI_STM32_EXTI9_5_IRQ_PRI,
__stm32_exti_isr_9_5, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI15_10,
CONFIG_EXTI_STM32_EXTI15_10_IRQ_PRI,
__stm32_exti_isr_15_10, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI16,
CONFIG_EXTI_STM32_EXTI16_IRQ_PRI,
__stm32_exti_isr_16, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI17,
CONFIG_EXTI_STM32_EXTI17_IRQ_PRI,
__stm32_exti_isr_17, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI18,
CONFIG_EXTI_STM32_EXTI18_IRQ_PRI,
__stm32_exti_isr_18, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI21,
CONFIG_EXTI_STM32_EXTI21_IRQ_PRI,
__stm32_exti_isr_21, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32F4_IRQ_EXTI22,
CONFIG_EXTI_STM32_EXTI22_IRQ_PRI,
__stm32_exti_isr_22, DEVICE_GET(exti_stm32),
0);
#elif defined(CONFIG_SOC_SERIES_STM32L0X)
IRQ_CONNECT(STM32L0_IRQ_EXTI0_1,
CONFIG_EXTI_STM32_EXTI1_0_IRQ_PRI,
__stm32_exti_isr_0_1, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32L0_IRQ_EXTI2_3,
CONFIG_EXTI_STM32_EXTI3_2_IRQ_PRI,
__stm32_exti_isr_2_3, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32L0_IRQ_EXTI4_15,
CONFIG_EXTI_STM32_EXTI15_4_IRQ_PRI,
__stm32_exti_isr_4_15, DEVICE_GET(exti_stm32),
0);
#elif defined(CONFIG_SOC_SERIES_STM32L4X)
IRQ_CONNECT(STM32L4_IRQ_EXTI0,
CONFIG_EXTI_STM32_EXTI0_IRQ_PRI,
__stm32_exti_isr_0, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32L4_IRQ_EXTI1,
CONFIG_EXTI_STM32_EXTI1_IRQ_PRI,
__stm32_exti_isr_1, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32L4_IRQ_EXTI2,
CONFIG_EXTI_STM32_EXTI2_IRQ_PRI,
__stm32_exti_isr_2, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32L4_IRQ_EXTI3,
CONFIG_EXTI_STM32_EXTI3_IRQ_PRI,
__stm32_exti_isr_3, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32L4_IRQ_EXTI4,
CONFIG_EXTI_STM32_EXTI4_IRQ_PRI,
__stm32_exti_isr_4, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32L4_IRQ_EXTI9_5,
CONFIG_EXTI_STM32_EXTI9_5_IRQ_PRI,
__stm32_exti_isr_9_5, DEVICE_GET(exti_stm32),
0);
IRQ_CONNECT(STM32L4_IRQ_EXTI15_10,
CONFIG_EXTI_STM32_EXTI15_10_IRQ_PRI,
__stm32_exti_isr_15_10, DEVICE_GET(exti_stm32),
0);
#endif
}