From 8bab36f1c5ffa4d0c32f788a467952160f9c8b4f Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 27 Aug 2021 22:59:43 +0200 Subject: [PATCH] cpu/stm32: Implement periph/gpio_ll{,_irq} except for STM32F1 Co-authored-by: Gunar Schorcht Co-authored-by: Alexandre Abadie --- cpu/stm32/Makefile.features | 7 + cpu/stm32/include/gpio_ll_arch.h | 208 +++++++++++++++++ cpu/stm32/include/periph/cpu_gpio_ll.h | 80 +++++++ cpu/stm32/include/periph_cpu.h | 1 + cpu/stm32/kconfigs/f0/Kconfig | 4 + cpu/stm32/kconfigs/f2/Kconfig | 4 + cpu/stm32/kconfigs/f3/Kconfig | 4 + cpu/stm32/kconfigs/f4/Kconfig | 4 + cpu/stm32/kconfigs/f7/Kconfig | 4 + cpu/stm32/kconfigs/g0/Kconfig | 4 + cpu/stm32/kconfigs/g4/Kconfig | 4 + cpu/stm32/kconfigs/l0/Kconfig | 6 +- cpu/stm32/kconfigs/l1/Kconfig | 6 +- cpu/stm32/kconfigs/l4/Kconfig | 4 + cpu/stm32/kconfigs/l5/Kconfig | 4 + cpu/stm32/kconfigs/mp1/Kconfig | 4 + cpu/stm32/kconfigs/u5/Kconfig | 4 + cpu/stm32/kconfigs/wb/Kconfig | 4 + cpu/stm32/kconfigs/wl/Kconfig | 4 + cpu/stm32/periph/gpio_ll.c | 186 ++++++++++++++++ cpu/stm32/periph/gpio_ll_irq.c | 294 +++++++++++++++++++++++++ 21 files changed, 838 insertions(+), 2 deletions(-) create mode 100644 cpu/stm32/include/gpio_ll_arch.h create mode 100644 cpu/stm32/include/periph/cpu_gpio_ll.h create mode 100644 cpu/stm32/periph/gpio_ll.c create mode 100644 cpu/stm32/periph/gpio_ll_irq.c diff --git a/cpu/stm32/Makefile.features b/cpu/stm32/Makefile.features index bedaa6971d..3f96dc100a 100644 --- a/cpu/stm32/Makefile.features +++ b/cpu/stm32/Makefile.features @@ -12,6 +12,13 @@ FEATURES_PROVIDED += periph_rtt_overflow FEATURES_PROVIDED += periph_uart_modecfg FEATURES_PROVIDED += periph_uart_nonblocking +ifneq ($(CPU_FAM),f1) + FEATURES_PROVIDED += periph_gpio_ll + FEATURES_PROVIDED += periph_gpio_ll_irq + FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_high + FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_low +endif + ifneq (,$(filter $(CPU_FAM),f0 f1 f3 g0 g4 l0 l1 l4 l5 u5 wb wl)) FEATURES_PROVIDED += periph_flashpage FEATURES_PROVIDED += periph_flashpage_in_address_space diff --git a/cpu/stm32/include/gpio_ll_arch.h b/cpu/stm32/include/gpio_ll_arch.h new file mode 100644 index 0000000000..96b6d1f68b --- /dev/null +++ b/cpu/stm32/include/gpio_ll_arch.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2016 Freie Universität Berlin + * 2017 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_stm32 + * @ingroup drivers_periph_gpio_ll + * @{ + * + * @file + * @brief CPU specific part of the Peripheral GPIO Low-Level API + * + * @author Hauke Petersen + * @author Vincent Dupont + */ + +#ifndef GPIO_LL_ARCH_H +#define GPIO_LL_ARCH_H + +#include "architecture.h" +#include "periph/gpio_ll.h" +#include "periph_cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DOXYGEN /* hide implementation specific details from Doxygen */ + +/** + * @brief Get a GPIO port by number + */ +#if defined(CPU_FAM_STM32MP1) +#define GPIO_PORT(num) (GPIOA_BASE + ((num) << 12)) +#else +#define GPIO_PORT(num) (GPIOA_BASE + ((num) << 10)) +#endif + +/** + * @brief Get a GPIO port number by gpio_t value + */ +#if defined(CPU_FAM_STM32MP1) +#define GPIO_PORT_NUM(port) (((port) - GPIOA_BASE) >> 12) +#else +#define GPIO_PORT_NUM(port) (((port) - GPIOA_BASE) >> 10) +#endif + +static inline uword_t gpio_ll_read(gpio_port_t port) +{ + GPIO_TypeDef *p = (GPIO_TypeDef *)port; + return p->IDR; +} + +static inline uword_t gpio_ll_read_output(gpio_port_t port) +{ + GPIO_TypeDef *p = (GPIO_TypeDef *)port; + return p->ODR; +} + +static inline void gpio_ll_set(gpio_port_t port, uword_t mask) +{ + GPIO_TypeDef *p = (GPIO_TypeDef *)port; + p->BSRR = mask; +} + +static inline void gpio_ll_clear(gpio_port_t port, uword_t mask) +{ + GPIO_TypeDef *p = (GPIO_TypeDef *)port; + p->BSRR = mask << 16; +} + +static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask) +{ + GPIO_TypeDef *p = (GPIO_TypeDef *)port; + unsigned irq_state = irq_disable(); + p->ODR ^= mask; + irq_restore(irq_state); +} + +static inline void gpio_ll_write(gpio_port_t port, uword_t value) +{ + GPIO_TypeDef *p = (GPIO_TypeDef *)port; + p->ODR = value; +} + +static inline gpio_port_t gpio_get_port(gpio_t pin) +{ + return pin & 0xfffffff0LU; +} + +static inline uint8_t gpio_get_pin_num(gpio_t pin) +{ + return pin & 0xfLU; +} + +static inline gpio_port_t gpio_port_pack_addr(void *addr) +{ + return (gpio_port_t)addr; +} + +static inline void * gpio_port_unpack_addr(gpio_port_t port) +{ + if (port < GPIOA_BASE) { + return (void *)port; + } + + return NULL; +} + +static inline bool is_gpio_port_num_valid(uint_fast8_t num) +{ + switch (num) { + default: + return false; +#ifdef GPIOA_BASE + case 0: +#endif +#ifdef GPIOB_BASE + case 1: +#endif +#ifdef GPIOC_BASE + case 2: +#endif +#ifdef GPIOD_BASE + case 3: +#endif +#ifdef GPIOE_BASE + case 4: +#endif +#ifdef GPIOF_BASE + case 5: +#endif +#ifdef GPIOG_BASE + case 6: +#endif +#ifdef GPIOH_BASE + case 7: +#endif +#ifdef GPIOI_BASE + case 8: +#endif +#ifdef GPIOJ_BASE + case 9: +#endif +#ifdef GPIOK_BASE + case 10: +#endif +#ifdef GPIOL_BASE + case 11: +#endif +#ifdef GPIOM_BASE + case 12: +#endif +#ifdef GPION_BASE + case 13: +#endif +#ifdef GPIOO_BASE + case 14: +#endif +#ifdef GPIOP_BASE + case 15: +#endif +#ifdef GPIOQ_BASE + case 16: +#endif +#ifdef GPIOR_BASE + case 17: +#endif +#ifdef GPIOS_BASE + case 18: +#endif +#ifdef GPIOT_BASE + case 19: +#endif +#ifdef GPIOU_BASE + case 20: +#endif +#ifdef GPIOV_BASE + case 21: +#endif +#ifdef GPIOW_BASE + case 22: +#endif +#ifdef GPIOX_BASE + case 23: +#endif +#ifdef GPIOY_BASE + case 24: +#endif +#ifdef GPIOZ_BASE + case 25: +#endif + return true; + } +} + +#endif /* DOXYGEN */ +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_LL_ARCH_H */ +/** @} */ diff --git a/cpu/stm32/include/periph/cpu_gpio_ll.h b/cpu/stm32/include/periph/cpu_gpio_ll.h new file mode 100644 index 0000000000..e3cd0e78e6 --- /dev/null +++ b/cpu/stm32/include/periph/cpu_gpio_ll.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2021 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_stm32 + * @{ + * + * @file + * @brief GPIO LL CPU definitions for the STM32 family + * + * @author Marian Buschsieweke + */ + +#ifndef PERIPH_CPU_GPIO_LL_H +#define PERIPH_CPU_GPIO_LL_H + +#include +#include "cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Hide this from Doxygen to avoid merging implementation details into + * public view on type */ +#ifndef DOXYGEN + +#define HAVE_GPIO_PULL_STRENGTH_T +typedef enum { + GPIO_PULL_WEAKEST = 0, + GPIO_PULL_WEAK = 0, + GPIO_PULL_STRONG = 0, + GPIO_PULL_STRONGEST = 0 +} gpio_pull_strength_t; + +#define HAVE_GPIO_DRIVE_STRENGTH_T +typedef enum { + GPIO_DRIVE_WEAKEST = 0, + GPIO_DRIVE_WEAK = 0, + GPIO_DRIVE_STRONG = 0, + GPIO_DRIVE_STRONGEST = 0 +} gpio_drive_strength_t; + +#define HAVE_GPIO_IRQ_TRIG_T +/* + * Layout: + * 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+ + * | RFU |T|L|H| + * +-+-+-+-+-+-+-+-+ + * + * RFU = Reserved for future use + * T = Trigger mode (1 = Level triggered, 0 = Edge triggered) + * L = Low (1 = low level / falling edge) + * H = High (1 = high level / rising edge) + * + * Note: This layout overlaps with gpio_flank_t by intent + */ +typedef enum { + GPIO_TRIGGER_EDGE_RISING = 0x1, + GPIO_TRIGGER_EDGE_FALLING = 0x2, + GPIO_TRIGGER_EDGE_BOTH = GPIO_TRIGGER_EDGE_RISING | GPIO_TRIGGER_EDGE_FALLING, + GPIO_TRIGGER_LEVEL = 0x4, + GPIO_TRIGGER_LEVEL_HIGH = GPIO_TRIGGER_LEVEL | GPIO_TRIGGER_EDGE_RISING, + GPIO_TRIGGER_LEVEL_LOW = GPIO_TRIGGER_LEVEL | GPIO_TRIGGER_EDGE_FALLING, +} gpio_irq_trig_t; + +#endif /* ndef Doxygen */ + +#ifdef __cplusplus +} +#endif + +#endif /* PERIPH_CPU_GPIO_LL_H */ +/** @} */ diff --git a/cpu/stm32/include/periph_cpu.h b/cpu/stm32/include/periph_cpu.h index e862625bf6..77cb3ac776 100644 --- a/cpu/stm32/include/periph_cpu.h +++ b/cpu/stm32/include/periph_cpu.h @@ -63,6 +63,7 @@ #include "periph/cpu_dma.h" #include "periph/cpu_eth.h" #include "periph/cpu_gpio.h" +#include "periph/cpu_gpio_ll.h" #include "periph/cpu_i2c.h" #include "periph/cpu_ltdc.h" #include "periph/cpu_pm.h" diff --git a/cpu/stm32/kconfigs/f0/Kconfig b/cpu/stm32/kconfigs/f0/Kconfig index b77840c956..a5ff5641e5 100644 --- a/cpu/stm32/kconfigs/f0/Kconfig +++ b/cpu/stm32/kconfigs/f0/Kconfig @@ -17,6 +17,10 @@ config CPU_FAM_F0 select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_FLASHPAGE_RAW + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/f2/Kconfig b/cpu/stm32/kconfigs/f2/Kconfig index 00a08c8fd0..fce3e13483 100644 --- a/cpu/stm32/kconfigs/f2/Kconfig +++ b/cpu/stm32/kconfigs/f2/Kconfig @@ -13,6 +13,10 @@ config CPU_FAM_F2 select HAS_CPU_STM32F2 select HAS_CORTEXM_MPU select HAS_PERIPH_FLASHPAGE + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM select HAS_PERIPH_VBAT diff --git a/cpu/stm32/kconfigs/f3/Kconfig b/cpu/stm32/kconfigs/f3/Kconfig index 53a20d8559..6a61d33467 100644 --- a/cpu/stm32/kconfigs/f3/Kconfig +++ b/cpu/stm32/kconfigs/f3/Kconfig @@ -14,6 +14,10 @@ config CPU_FAM_F3 select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_FLASHPAGE_RAW + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_RTC_MEM select HAS_PERIPH_VBAT select HAS_PERIPH_WDT diff --git a/cpu/stm32/kconfigs/f4/Kconfig b/cpu/stm32/kconfigs/f4/Kconfig index 889bebdb62..4ee8d84927 100644 --- a/cpu/stm32/kconfigs/f4/Kconfig +++ b/cpu/stm32/kconfigs/f4/Kconfig @@ -12,6 +12,10 @@ config CPU_FAM_F4 select HAS_CPU_STM32F4 select HAS_CORTEXM_MPU select HAS_PERIPH_FLASHPAGE + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_RTC_MEM select HAS_PERIPH_VBAT select HAS_PERIPH_WDT diff --git a/cpu/stm32/kconfigs/f7/Kconfig b/cpu/stm32/kconfigs/f7/Kconfig index 4e3e0187d0..79bfb37d6a 100644 --- a/cpu/stm32/kconfigs/f7/Kconfig +++ b/cpu/stm32/kconfigs/f7/Kconfig @@ -13,6 +13,10 @@ config CPU_FAM_F7 select HAS_CPU_STM32F7 select HAS_CORTEXM_MPU select HAS_PERIPH_FLASHPAGE + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM select HAS_PERIPH_VBAT diff --git a/cpu/stm32/kconfigs/g0/Kconfig b/cpu/stm32/kconfigs/g0/Kconfig index ad722cceb4..680376770b 100644 --- a/cpu/stm32/kconfigs/g0/Kconfig +++ b/cpu/stm32/kconfigs/g0/Kconfig @@ -14,6 +14,10 @@ config CPU_FAM_G0 select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_FLASHPAGE_RAW + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/g4/Kconfig b/cpu/stm32/kconfigs/g4/Kconfig index 3648768eb4..030751d840 100644 --- a/cpu/stm32/kconfigs/g4/Kconfig +++ b/cpu/stm32/kconfigs/g4/Kconfig @@ -14,6 +14,10 @@ config CPU_FAM_G4 select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_HWRNG select HAS_PERIPH_VBAT select HAS_PERIPH_WDT diff --git a/cpu/stm32/kconfigs/l0/Kconfig b/cpu/stm32/kconfigs/l0/Kconfig index 27d18d5be8..fa975a163b 100644 --- a/cpu/stm32/kconfigs/l0/Kconfig +++ b/cpu/stm32/kconfigs/l0/Kconfig @@ -10,10 +10,14 @@ config CPU_FAM_L0 select CPU_STM32 select CPU_CORE_CORTEX_M0PLUS select HAS_CPU_STM32L0 + select HAS_PERIPH_EEPROM select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE - select HAS_PERIPH_EEPROM + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_RTC_MEM select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/l1/Kconfig b/cpu/stm32/kconfigs/l1/Kconfig index 21504b9a79..f5cb9142c4 100644 --- a/cpu/stm32/kconfigs/l1/Kconfig +++ b/cpu/stm32/kconfigs/l1/Kconfig @@ -11,10 +11,14 @@ config CPU_FAM_L1 select CPU_CORE_CORTEX_M3 select HAS_CPU_STM32L1 select HAS_CORTEXM_MPU + select HAS_PERIPH_EEPROM select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE - select HAS_PERIPH_EEPROM + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_RTC_MEM select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/l4/Kconfig b/cpu/stm32/kconfigs/l4/Kconfig index 4247d172ef..cd89902fc7 100644 --- a/cpu/stm32/kconfigs/l4/Kconfig +++ b/cpu/stm32/kconfigs/l4/Kconfig @@ -14,6 +14,10 @@ config CPU_FAM_L4 select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM select HAS_PERIPH_VBAT diff --git a/cpu/stm32/kconfigs/l5/Kconfig b/cpu/stm32/kconfigs/l5/Kconfig index d302634b8d..7622571149 100644 --- a/cpu/stm32/kconfigs/l5/Kconfig +++ b/cpu/stm32/kconfigs/l5/Kconfig @@ -13,6 +13,10 @@ config CPU_FAM_L5 select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM select HAS_PERIPH_VBAT diff --git a/cpu/stm32/kconfigs/mp1/Kconfig b/cpu/stm32/kconfigs/mp1/Kconfig index 8bd8cf6777..28741fe36d 100644 --- a/cpu/stm32/kconfigs/mp1/Kconfig +++ b/cpu/stm32/kconfigs/mp1/Kconfig @@ -10,6 +10,10 @@ config CPU_FAM_MP1 select CPU_CORE_CORTEX_M4F select HAS_CORTEXM_MPU select HAS_CPU_STM32MP1 + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW config CPU_FAM default "mp1" if CPU_FAM_MP1 diff --git a/cpu/stm32/kconfigs/u5/Kconfig b/cpu/stm32/kconfigs/u5/Kconfig index 5bcac0efb4..fdfd90bf3b 100644 --- a/cpu/stm32/kconfigs/u5/Kconfig +++ b/cpu/stm32/kconfigs/u5/Kconfig @@ -14,6 +14,10 @@ config CPU_FAM_U5 select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM select HAS_PERIPH_VBAT diff --git a/cpu/stm32/kconfigs/wb/Kconfig b/cpu/stm32/kconfigs/wb/Kconfig index 81ae240ce4..d8a4f74cb0 100644 --- a/cpu/stm32/kconfigs/wb/Kconfig +++ b/cpu/stm32/kconfigs/wb/Kconfig @@ -13,6 +13,10 @@ config CPU_FAM_WB select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM select HAS_PERIPH_VBAT diff --git a/cpu/stm32/kconfigs/wl/Kconfig b/cpu/stm32/kconfigs/wl/Kconfig index 3ab1e02b8b..a8fb760eb5 100644 --- a/cpu/stm32/kconfigs/wl/Kconfig +++ b/cpu/stm32/kconfigs/wl/Kconfig @@ -14,6 +14,10 @@ config CPU_FAM_WL select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE + select HAS_PERIPH_GPIO_LL + select HAS_PERIPH_GPIO_LL_IRQ + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH + select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW select HAS_PERIPH_RTC_MEM select HAS_PERIPH_VBAT select HAS_PERIPH_WDT diff --git a/cpu/stm32/periph/gpio_ll.c b/cpu/stm32/periph/gpio_ll.c new file mode 100644 index 0000000000..25543ef10c --- /dev/null +++ b/cpu/stm32/periph/gpio_ll.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2014-2015 Freie Universität Berlin + * 2015 Hamburg University of Applied Sciences + * 2017-2020 Inria + * 2017 OTA keys S.A. + * 2021 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup cpu_stm32 + * @ingroup drivers_periph_gpio_ll + * @{ + * + * @file + * @brief GPIO Low-level API implementation for the STM32 GPIO peripheral (except F1) + * + * @author Hauke Petersen + * @author Fabian Nack + * @author Alexandre Abadie + * @author Katja Kirstein + * @author Vincent Dupont + * @author Marian Buschsieweke + * + * @} + */ + +#include +#include + +#include "cpu.h" +#include "bitarithm.h" +#include "periph/gpio_ll.h" + +#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3) || defined(CPU_FAM_STM32L1) +# define GPIO_BUS AHB +# define GPIOAEN RCC_AHBENR_GPIOAEN +#elif defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32G0) +# define GPIO_BUS IOP +# define GPIOAEN RCC_IOPENR_GPIOAEN +#elif defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32WB) || \ + defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32L5) || \ + defined(CPU_FAM_STM32U5) || defined(CPU_FAM_STM32WL) +#define GPIO_BUS AHB2 +# if defined(CPU_FAM_STM32U5) +# define GPIOAEN RCC_AHB2ENR1_GPIOAEN +# else +# define GPIOAEN RCC_AHB2ENR_GPIOAEN +# endif +# ifdef PWR_CR2_IOSV +# define PORTG_REQUIRES_EXTERNAL_POWER +# endif +#elif defined(CPU_FAM_STM32MP1) +# define GPIO_BUS AHB4 +# define GPIOAEN RCC_MC_AHB4ENSETR_GPIOAEN +#else +# define GPIO_BUS AHB1 +# define GPIOAEN RCC_AHB1ENR_GPIOAEN +#endif + +static void _init_clock(gpio_port_t port) +{ + periph_clk_en(GPIO_BUS, (GPIOAEN << GPIO_PORT_NUM(port))); +#ifdef PORTG_REQUIRES_EXTERNAL_POWER + if (port == (uintptr_t)GPIOG) { + /* Port G requires external power supply */ + periph_clk_en(APB1, RCC_APB1ENR1_PWREN); + PWR->CR2 |= PWR_CR2_IOSV; + } +#endif +} + +static void _set_dir(gpio_port_t port, uint8_t pin, bool output) +{ + GPIO_TypeDef *p = (void *)port; + uint32_t tmp = p->MODER; + tmp &= ~(0x3 << (2 * pin)); + if (output) { + tmp |= 1UL << (2 * pin); + } + p->MODER = tmp; +} + +static void _set_output_type(gpio_port_t port, uint8_t pin, bool open_drain) +{ + GPIO_TypeDef *p = (void *)port; + if (open_drain) { + p->OTYPER |= 1UL << pin; + } + else { + p->OTYPER &= ~(1UL << pin); + } +} + +static void _set_pull_config(gpio_port_t port, uint8_t pin, gpio_pull_t pull) +{ + GPIO_TypeDef *p = (void *)port; + /* being more verbose here so that compiler doesn't generate two loads and stores when accessing + * volatile variable */ + uint32_t pupdr = p->PUPDR; + pupdr &= ~(0x3UL << (2 * pin)); + pupdr |= (uint32_t)pull << (2 * pin); + p->PUPDR = pupdr; +} + +static void _set_slew_rate(gpio_port_t port, uint8_t pin, gpio_slew_t slew_rate) +{ + GPIO_TypeDef *p = (void *)port; + /* being more verbose here so that compiler doesn't generate two loads and + * stores when accessing volatile variable */ + uint32_t ospeedr = p->OSPEEDR; + ospeedr &= ~(3UL << (2 * pin)); + ospeedr |= (uint32_t)slew_rate << (2 * pin); + p->OSPEEDR = ospeedr; +} + +int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf) +{ + if ((conf->pull == GPIO_PULL_KEEP) || (conf->state == GPIO_OUTPUT_OPEN_SOURCE)) { + return -ENOTSUP; + } + + unsigned state = irq_disable(); + _init_clock(port); + if (conf->initial_value) { + gpio_ll_set(port, 1UL << pin); + } + else { + gpio_ll_clear(port, 1UL << pin); + } + _set_output_type(port, pin, conf->state == GPIO_OUTPUT_OPEN_DRAIN); + _set_pull_config(port, pin, conf->pull); + _set_slew_rate(port, pin, conf->slew_rate); + _set_dir(port, pin, conf->state < GPIO_INPUT); + irq_restore(state); + + return 0; +} + +static gpio_state_t _get_state(gpio_port_t port, uint8_t pin) +{ + GPIO_TypeDef *p = (void *)port; + uint32_t moder = (p->MODER >> (2 * pin)) & 0x3UL; + switch (moder) { + case 0: + return GPIO_INPUT; + case 1: + return ((p->OTYPER >> pin) & 0x1UL) ? GPIO_OUTPUT_OPEN_DRAIN + : GPIO_OUTPUT_PUSH_PULL; + } + return GPIO_USED_BY_PERIPHERAL; +} + +static gpio_pull_t _get_pull_config(gpio_port_t port, uint8_t pin) +{ + GPIO_TypeDef *p = (void *)port; + uint32_t pupdr = (p->PUPDR >> (2 * pin)) & 0x3UL; + return (gpio_pull_t)pupdr; +} + +static gpio_slew_t _get_slew_rate(gpio_port_t port, uint8_t pin) +{ + GPIO_TypeDef *p = (void *)port; + uint32_t ospeedr = (p->OSPEEDR >> (2 * pin)) & 0x3UL; + return (gpio_slew_t)ospeedr; +} + +void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin) +{ + assert(dest); + unsigned state = irq_disable(); + memset(dest, 0, sizeof(*dest)); + dest->state = _get_state(port, pin); + dest->pull = _get_pull_config(port, pin); + dest->slew_rate = _get_slew_rate(port, pin); + if (dest->state == GPIO_INPUT) { + dest->initial_value = (gpio_ll_read(port) >> pin) & 1UL; + } + else { + dest->initial_value = (gpio_ll_read_output(port) >> pin) & 1UL; + } + irq_restore(state); +} diff --git a/cpu/stm32/periph/gpio_ll_irq.c b/cpu/stm32/periph/gpio_ll_irq.c new file mode 100644 index 0000000000..c9eb11129f --- /dev/null +++ b/cpu/stm32/periph/gpio_ll_irq.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2014-2015 Freie Universität Berlin + * 2015 Hamburg University of Applied Sciences + * 2017-2020 Inria + * 2017 OTA keys S.A. + * 2021 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup cpu_stm32 + * @ingroup drivers_periph_gpio_ll_irq + * @{ + * + * @file + * @brief IRQ implementation of the GPIO Low-Level API for STM32 (except F1) + * + * @author Hauke Petersen + * @author Fabian Nack + * @author Alexandre Abadie + * @author Katja Kirstein + * @author Vincent Dupont + * @author Marian Buschsieweke + * + * @} + */ + +#include + +#include "cpu.h" +#include "bitarithm.h" +#include "periph/gpio_ll_irq.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#define EXTI_NUMOF (16U) +#define EXTI_MASK (0xFFFF) + +#if defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32WB) || \ + defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32G0) || \ + defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32U5) || \ + defined(CPU_FAM_STM32WL) +# define EXTI_REG_RTSR (EXTI->RTSR1) +# define EXTI_REG_FTSR (EXTI->FTSR1) +# define EXTI_REG_SWIER (EXTI->SWIER1) +# define EXTI_REG_IMR (EXTI->IMR1) +# if !defined(CPU_FAM_STM32G0) && !defined(CPU_FAM_STM32L5) && \ + !defined(CPU_FAM_STM32U5) && !defined(CPU_FAM_STM32MP1) +# define EXTI_REG_PR (EXTI->PR1) +# endif +#elif defined(CPU_FAM_STM32MP1) +# define EXTI_REG_RTSR (EXTI->RTSR1) +# define EXTI_REG_FTSR (EXTI->FTSR1) +# define EXTI_REG_PR (EXTI->PR1) +# define EXTI_REG_SWIER (EXTI->SWIER1) +# define EXTI_REG_IMR (EXTI_C2->IMR1) +#else +# define EXTI_REG_RTSR (EXTI->RTSR) +# define EXTI_REG_FTSR (EXTI->FTSR) +# define EXTI_REG_PR (EXTI->PR) +# define EXTI_REG_SWIER (EXTI->SWIER) +# define EXTI_REG_IMR (EXTI->IMR) +#endif + +void gpio_ll_irq_mask(gpio_port_t port, uint8_t pin) +{ + (void)port; + EXTI_REG_IMR &= ~(1 << pin); +} + +void gpio_ll_irq_unmask_and_clear(gpio_port_t port, uint8_t pin) +{ + (void)port; + EXTI_REG_IMR |= (1 << pin); +} + +struct isr_ctx { + gpio_ll_cb_t cb; + void *arg; +}; + +static struct isr_ctx isr_ctx[EXTI_NUMOF]; +static uint16_t level_triggered; + +static IRQn_Type get_irqn(uint8_t pin) +{ +#if defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32U5) + return EXTI0_IRQn + pin; +#elif defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L0) || \ + defined(CPU_FAM_STM32G0) + if (pin < 2) { + return EXTI0_1_IRQn; + } + else if (pin < 4) { + return EXTI2_3_IRQn; + } + else { + return EXTI4_15_IRQn; + } +#elif defined(CPU_FAM_STM32MP1) + if (pin < 5) { + return EXTI0_IRQn + pin; + } + else if (pin < 6) { + return EXTI5_IRQn; + } + else if (pin < 10) { + return EXTI6_IRQn + pin - 6; + } + else if (pin < 11) { + return EXTI10_IRQn; + } + else if (pin < 12) { + return EXTI11_IRQn; + } + else if (pin < 14) { + return EXTI12_IRQn + pin - 12; + } + else if (pin < 15) { + return EXTI14_IRQn; + } + else { + return EXTI15_IRQn; + } +#else + if (pin < 5) { + return EXTI0_IRQn + pin; + } + else if (pin < 10) { + return EXTI9_5_IRQn; + } + else { + return EXTI15_10_IRQn; + } +#endif +} + +static void clear_pending_irqs(uint8_t pin) +{ +#if defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) || \ + defined(CPU_FAM_STM32U5) || defined(CPU_FAM_STM32MP1) + /* clear any pending requests */ + EXTI->RPR1 = (1 << pin); + EXTI->FPR1 = (1 << pin); +#else + /* clear any pending requests */ + EXTI_REG_PR = (1 << pin); +#endif +} + +static void set_exti_port(uint8_t exti_num, uint8_t port_num) +{ +#if defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) || \ + defined(CPU_FAM_STM32U5) + /* enable specific pin as exti sources */ + EXTI->EXTICR[exti_num >> 2] &= ~(0xf << ((exti_num & 0x03) * 8)); + EXTI->EXTICR[exti_num >> 2] |= (port_num << ((exti_num & 0x03) * 8)); +#elif defined(CPU_FAM_STM32MP1) + /* enable specific pin as exti sources */ + EXTI->EXTICR[exti_num >> 2] &= ~(0xf << ((exti_num & 0x03) * 4)); + EXTI->EXTICR[exti_num >> 2] |= (port_num << ((exti_num & 0x03) * 4)); +#else + /* enable specific pin as exti sources */ + SYSCFG->EXTICR[exti_num >> 2] &= ~(0xf << ((exti_num & 0x03) * 4)); + SYSCFG->EXTICR[exti_num >> 2] |= (port_num << ((exti_num & 0x03) * 4)); +#endif +} + +static uint8_t get_exti_port(uint8_t exti_num) +{ +#if defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) || \ + defined(CPU_FAM_STM32U5) + /* enable specific pin as exti sources */ + return 0xf & (EXTI->EXTICR[exti_num >> 2] >> ((exti_num & 0x03) * 8)); +#elif defined(CPU_FAM_STM32MP1) + /* enable specific pin as exti sources */ + return 0xf & (EXTI->EXTICR[exti_num >> 2] >> ((exti_num & 0x03) * 4)); +#else + /* enable specific pin as exti sources */ + return 0xf & (SYSCFG->EXTICR[exti_num >> 2] >> ((exti_num & 0x03) * 4)); +#endif +} + +int gpio_ll_irq(gpio_port_t port, uint8_t pin, gpio_irq_trig_t trig, gpio_ll_cb_t cb, void *arg) +{ + unsigned irq_state = irq_disable(); + int port_num = GPIO_PORT_NUM(port); + + /* set callback */ + isr_ctx[pin].cb = cb; + isr_ctx[pin].arg = arg; + + /* enable clock of the SYSCFG module for EXTI configuration */ +#if !defined(CPU_FAM_STM32WB) && !defined(CPU_FAM_STM32MP1) && \ + !defined(CPU_FAM_STM32WL) +#ifdef CPU_FAM_STM32F0 + periph_clk_en(APB2, RCC_APB2ENR_SYSCFGCOMPEN); +#elif defined(CPU_FAM_STM32G0) + periph_clk_en(APB12, RCC_APBENR2_SYSCFGEN); +#elif defined(CPU_FAM_STM32U5) + periph_clk_en(APB3, RCC_APB3ENR_SYSCFGEN); +#else + periph_clk_en(APB2, RCC_APB2ENR_SYSCFGEN); +#endif +#endif + + /* enable global pin interrupt */ + NVIC_EnableIRQ(get_irqn(pin)); + + /* configure trigger */ + if (trig & GPIO_TRIGGER_EDGE_RISING) { + EXTI_REG_RTSR |= 1UL << pin; + } + else { + EXTI_REG_RTSR &= ~(1UL << pin); + } + if (trig & GPIO_TRIGGER_EDGE_FALLING) { + EXTI_REG_FTSR |= 1UL << pin; + } + else { + EXTI_REG_FTSR &= ~(1UL << pin); + } + + set_exti_port(pin, port_num); + + clear_pending_irqs(pin); + gpio_ll_irq_unmask_and_clear(port, pin); + + if (trig & GPIO_TRIGGER_LEVEL) { + level_triggered |= 1UL << pin; + /* if input is already at trigger level there might be no flank, so issue soft IRQ */ + uint32_t actual_level = gpio_ll_read(port) & (1UL << pin); + uint32_t trigger_level = EXTI_REG_RTSR & (1UL << pin); + if (actual_level == trigger_level) { + EXTI_REG_SWIER = 1UL << pin; + } + } + else { + level_triggered &= ~(1UL << pin); + } + + irq_restore(irq_state); + + return 0; +} + +void isr_exti(void) +{ +#if defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) || \ + defined(CPU_FAM_STM32U5) || defined(CPU_FAM_STM32MP1) + /* get all interrupts handled by this ISR */ + uint32_t pending_rising_isr = (EXTI->RPR1 & EXTI_MASK); + uint32_t pending_falling_isr = (EXTI->FPR1 & EXTI_MASK); + + /* clear by writing a 1 */ + EXTI->RPR1 = pending_rising_isr; + EXTI->FPR1 = pending_falling_isr; + + /* only generate interrupts against lines which have their IMR set */ + uint32_t pending_isr = (pending_rising_isr | pending_falling_isr) & EXTI_REG_IMR; +#else + /* read all pending interrupts wired to isr_exti */ + uint32_t pending_isr = (EXTI_REG_PR & EXTI_MASK); + + /* clear by writing a 1 */ + EXTI_REG_PR = pending_isr; + + /* only generate soft interrupts against lines which have their IMR set */ + pending_isr &= EXTI_REG_IMR; +#endif + + /* iterate over all set bits */ + uint8_t pin = 0; + while (pending_isr) { + pending_isr = bitarithm_test_and_clear(pending_isr, &pin); + isr_ctx[pin].cb(isr_ctx[pin].arg); + /* emulate level triggered IRQs by asserting the IRQ again in software, if needed */ + if (level_triggered & (1UL << pin)) { + /* Trading a couple of CPU cycles to not having to store port connected to EXTI in RAM. + * A simple look up table would save ~6 instructions for the cost 64 byte or RAM. */ + gpio_port_t port = GPIO_PORT(get_exti_port(pin)); + uint32_t actual_level = gpio_ll_read(port) & (1UL << pin); + uint32_t trigger_level = EXTI_REG_RTSR & (1UL << pin); + if (actual_level == trigger_level) { + EXTI_REG_SWIER = 1UL << pin; + } + } + } + cortexm_isr_end(); +}