From 33907674dda9521d5628a403a8c85c06b13f0e53 Mon Sep 17 00:00:00 2001 From: Maciek Borzecki Date: Mon, 14 Mar 2016 16:29:46 +0100 Subject: [PATCH] interupt_controller/stm32_exti: driver for STM32 EXTI controller Introcuce a driver for External Interrupt/Event Controller (EXTI) found on STM32 MCUs. Change-Id: Ib206521fcc51b5dfaaf5dea9d436f8304f3a36be Origin: Original Signed-off-by: Maciej Borzecki --- arch/arm/soc/st_stm32/stm32f1/soc.h | 3 + drivers/interrupt_controller/Kconfig | 3 + drivers/interrupt_controller/Kconfig.stm32 | 75 ++++++ drivers/interrupt_controller/Makefile | 2 + drivers/interrupt_controller/exti_stm32.c | 281 +++++++++++++++++++++ drivers/interrupt_controller/exti_stm32.h | 88 +++++++ 6 files changed, 452 insertions(+) create mode 100644 drivers/interrupt_controller/Kconfig.stm32 create mode 100644 drivers/interrupt_controller/exti_stm32.c create mode 100644 drivers/interrupt_controller/exti_stm32.h diff --git a/arch/arm/soc/st_stm32/stm32f1/soc.h b/arch/arm/soc/st_stm32/stm32f1/soc.h index 3a3e6c62ab2..91ccfa76bd1 100644 --- a/arch/arm/soc/st_stm32/stm32f1/soc.h +++ b/arch/arm/soc/st_stm32/stm32f1/soc.h @@ -55,6 +55,9 @@ /* base address for where GPIO registers start */ #define GPIO_PORTS_BASE (GPIOA_BASE) +/* EXTI */ +#define EXTI_BASE (APB2PERIPH_BASE + 0x0400) + #ifndef _ASMLANGUAGE #include diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig index 82d2fe1bd76..2da2c4e3265 100644 --- a/drivers/interrupt_controller/Kconfig +++ b/drivers/interrupt_controller/Kconfig @@ -112,4 +112,7 @@ config ARCV2_INTERRUPT_UNIT The interrupt unit is optional in the ARCv2-based processors. When building a processor, you can configure the processor to include an interrupt unit. The ARCv2 interrupt unit is highly programmable. + +source "drivers/interrupt_controller/Kconfig.stm32" + endmenu diff --git a/drivers/interrupt_controller/Kconfig.stm32 b/drivers/interrupt_controller/Kconfig.stm32 new file mode 100644 index 00000000000..d0a6efdff3b --- /dev/null +++ b/drivers/interrupt_controller/Kconfig.stm32 @@ -0,0 +1,75 @@ +# Kconfig - STM32 EXTI configuration +# +# Copyright (c) 2016 Open-RnD Sp. z o.o. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if SOC_STM32 + +config EXTI_STM32 + bool "External Interrupt/Event Controller (EXTI) Driver for STM32 family of MCUs" + default y if SOC_STM32 + help + Enable EXTI driver for STM32 line of MCUs + +config EXTI_STM32_EXTI0_IRQ_PRI + int "EXTI0 IRQ priority" + depends on EXTI_STM32 + default 0 + help + IRQ priority of EXTI0 interrupt + +config EXTI_STM32_EXTI1_IRQ_PRI + int "EXTI1 IRQ priority" + depends on EXTI_STM32 + default 0 + help + IRQ priority of EXTI1 interrupt + +config EXTI_STM32_EXTI2_IRQ_PRI + int "EXTI2 IRQ priority" + depends on EXTI_STM32 + default 0 + help + IRQ priority of EXTI2 interrupt + +config EXTI_STM32_EXTI3_IRQ_PRI + int "EXTI3 IRQ priority" + depends on EXTI_STM32 + default 0 + help + IRQ priority of EXTI3 interrupt + +config EXTI_STM32_EXTI4_IRQ_PRI + int "EXTI4 IRQ priority" + depends on EXTI_STM32 + default 0 + help + IRQ priority of EXTI4 interrupt + +config EXTI_STM32_EXTI9_5_IRQ_PRI + int "EXTI9:5 IRQ priority" + depends on EXTI_STM32 + default 0 + help + IRQ priority of EXTI9:5 interrupt + +config EXTI_STM32_EXTI15_10_IRQ_PRI + int "EXTI15:10 IRQ priority" + depends on EXTI_STM32 + default 0 + help + IRQ priority of EXTI15:10 interrupt + +endif # SOC_STM32 diff --git a/drivers/interrupt_controller/Makefile b/drivers/interrupt_controller/Makefile index e6677386cd6..1f44d4d2dd2 100644 --- a/drivers/interrupt_controller/Makefile +++ b/drivers/interrupt_controller/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_IOAPIC) += ioapic_intr.o obj-$(CONFIG_LOAPIC_SPURIOUS_VECTOR) += loapic_spurious.o obj-$(CONFIG_ARCV2_INTERRUPT_UNIT) += arcv2_irq_unit.o + +obj-$(CONFIG_SOC_STM32) += exti_stm32.o diff --git a/drivers/interrupt_controller/exti_stm32.c b/drivers/interrupt_controller/exti_stm32.c new file mode 100644 index 00000000000..3242c8eabf9 --- /dev/null +++ b/drivers/interrupt_controller/exti_stm32.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2016 Open-RnD Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Driver for External interrupt/event controller in STM32 MCUs + * + * Based on reference manual: + * STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx + * advanced ARM ® -based 32-bit MCUs + * + * Chapter 10.2: External interrupt/event controller (EXTI) + * + */ +#include +#include +#include +#include "exti_stm32.h" + + +/* 10.3.7 EXTI register map */ +struct stm32_exti { + /* EXTI_IMR */ + uint32_t imr; + /* EXTI_EMR */ + uint32_t emr; + /* EXTI_RTSR */ + uint32_t rtsr; + /* EXTI_FTSR */ + uint32_t ftsr; + /* EXTI_SWIER */ + uint32_t swier; + /* EXTI_PR */ + uint32_t pr; +}; + +/* wrapper for user callback */ +struct __exti_cb { + stm32_exti_callback_t cb; + void *data; +}; + +#ifdef CONFIG_SOC_STM32F1X +#define EXTI_LINES 19 +#endif + +/* driver data */ +struct stm32_exti_data { + /* per-line callbacks */ + struct __exti_cb cb[EXTI_LINES]; +}; + + +#define AS_EXTI(__base_addr) \ + ((struct stm32_exti *)(__base_addr)) + +void stm32_exti_enable(struct device *dev, int line) +{ + volatile struct stm32_exti *exti = AS_EXTI(EXTI_BASE); + int irqnum; + + ARG_UNUSED(dev); + + exti->imr |= 1 << line; + +#ifdef CONFIG_SOC_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; + } +#endif + + irq_enable(irqnum); +} + +void stm32_exti_disable(struct device *dev, int line) +{ + volatile struct stm32_exti *exti = AS_EXTI(EXTI_BASE); + + ARG_UNUSED(dev); + + 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 = AS_EXTI(EXTI_BASE); + + 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 = AS_EXTI(EXTI_BASE); + + exti->pr |= 1 << line; +} + +void stm32_exti_trigger(struct device *dev, int line, int trigger) +{ + volatile struct stm32_exti *exti = AS_EXTI(EXTI_BASE); + + ARG_UNUSED(dev); + + if (trigger & STM32_EXTI_TRIG_RISING) { + exti->rtsr |= 1 << line; + } + + if (trigger & STM32_EXTI_TRIG_FALLING) { + exti->ftsr |= 1 << line; + } +} + +void stm32_exti_set_callback(struct device *dev, int line, + stm32_exti_callback_t cb, void *arg) +{ + 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(struct device *dev, int line) +{ + struct stm32_exti_data *data = dev->driver_data; + + data->cb[line].cb = NULL; + data->cb[line].data = NULL; +} + +/** + * @brief EXTI ISR handler + * + * Check EXTI lines in range @min @max for pending interrupts + * + * @param arg isr argument + * @parram min low end of EXTI# range + * @parram 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); + } + } +} + +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); +} + +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, + PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); + +/** + * @brief connect all interrupts + */ +static void __stm32_exti_connect_irqs(struct device *dev) +{ +#ifdef CONFIG_SOC_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); +#endif +} + + diff --git a/drivers/interrupt_controller/exti_stm32.h b/drivers/interrupt_controller/exti_stm32.h new file mode 100644 index 00000000000..a04dea8281a --- /dev/null +++ b/drivers/interrupt_controller/exti_stm32.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016 Open-RnD Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Driver for External interrupt/event controller in STM32 MCUs + * + * Based on reference manual: + * STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx + * advanced ARM ® -based 32-bit MCUs + * + * Chapter 8.2: External interrupt/event controller (EXTI) + * + */ + +#ifndef _STM32_EXTI_H_ +#define _STM32_EXTI_H_ + +#include + +/* device name */ +#define STM32_EXTI_NAME "stm32-exti" + +/** + * @brief enable EXTI interrupt for specific line + * + * @param line EXTI# line + */ +void stm32_exti_enable(struct device *dev, int line); + +/** + * @brief disable EXTI interrupt for specific line + * + * @param line EXTI# line + */ +void stm32_exti_disable(struct device *dev, int line); + +/** + * @brief EXTI trigger flags + */ +enum stm32_exti_trigger { + /* trigger on rising edge */ + STM32_EXTI_TRIG_RISING = 0x1, + /* trigger on falling endge */ + STM32_EXTI_TRIG_FALLING = 0x2, +}; + +/** + * @brief set EXTI interrupt line triggers + * + * @param line EXTI# line + * @param trg OR'ed stm32_exti_trigger flags + */ +void stm32_exti_trigger(struct device *dev, int line, int trg); + +/* callback for exti interrupt */ +typedef void (*stm32_exti_callback_t) (int line, void *user); + +/** + * @brief set EXTI interrupt callback + * + * @param line EXI# line + * @param cb user callback + * @param arg user arg + */ +void stm32_exti_set_callback(struct device *dev, int line, + stm32_exti_callback_t cb, void *data); + +/** + * @brief unset EXTI interrupt callback + * + * @param line EXI# line + */ +void stm32_exti_unset_callback(struct device *dev, int line); + +#endif /* _STM32_EXTI_H_ */