From cf59106433bc592437aa182e06ab73246671c498 Mon Sep 17 00:00:00 2001 From: Ricardo Salveti Date: Thu, 27 Oct 2016 00:08:34 +0530 Subject: [PATCH] gpio: stm32: add support for STM32F4 Implements MCU-specific GPIO input interrupt integration. Added definition of System configuration controller as well as its needed by the GPIO code. The SYSCFG controller is used for system-specific configuration such as: - remap the type of memory accessible at address 0x00000000 - manage the external interrupt line connection to GPIOs - configure the I/O compensation cell Change-Id: Id2ebfbd1b21e77be76406d1cd6cd5d4989e9e2fa Signed-off-by: Ricardo Salveti Signed-off-by: Amit Kucheria Signed-off-by: Kumar Gala --- arch/arm/soc/st_stm32/stm32f4/Makefile | 2 + .../arm/soc/st_stm32/stm32f4/gpio_registers.h | 62 ++++ arch/arm/soc/st_stm32/stm32f4/soc.h | 23 ++ arch/arm/soc/st_stm32/stm32f4/soc_gpio.c | 274 ++++++++++++++++++ arch/arm/soc/st_stm32/stm32f4/soc_registers.h | 1 + drivers/gpio/gpio_stm32.c | 36 +++ drivers/gpio/gpio_stm32.h | 4 + 7 files changed, 402 insertions(+) create mode 100644 arch/arm/soc/st_stm32/stm32f4/gpio_registers.h create mode 100644 arch/arm/soc/st_stm32/stm32f4/soc_gpio.c diff --git a/arch/arm/soc/st_stm32/stm32f4/Makefile b/arch/arm/soc/st_stm32/stm32f4/Makefile index fd8d5a50505..1ef7b4af355 100644 --- a/arch/arm/soc/st_stm32/stm32f4/Makefile +++ b/arch/arm/soc/st_stm32/stm32f4/Makefile @@ -1,4 +1,6 @@ obj-y += soc.o +obj-$(CONFIG_GPIO) += soc_gpio.o + zephyr: $(KERNEL_HEX_NAME) all: $(KERNEL_HEX_NAME) diff --git a/arch/arm/soc/st_stm32/stm32f4/gpio_registers.h b/arch/arm/soc/st_stm32/stm32f4/gpio_registers.h new file mode 100644 index 00000000000..a750ebdef55 --- /dev/null +++ b/arch/arm/soc/st_stm32/stm32f4/gpio_registers.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 Linaro Limited. + * + * 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. + */ + +#ifndef _STM32F4X_GPIO_REGISTERS_H_ +#define _STM32F4X_GPIO_REGISTERS_H_ + +/** + * @brief Driver for GPIO of STM32F4X family processor. + * + * Based on reference manual: + * RM0368 Reference manual STM32F401xB/C and STM32F401xD/E + * advanced ARM ® -based 32-bit MCUs + * + * Chapter 8: General-purpose I/Os (GPIOs) + */ + +/* 8.4 GPIO registers - each GPIO port controls 16 pins */ +struct stm32f4x_gpio { + uint32_t mode; + uint32_t otype; + uint32_t ospeed; + uint32_t pupdr; + uint32_t idr; + uint32_t odr; + uint32_t bsr; + uint32_t lck; + uint32_t afr[2]; +}; + +union syscfg_exticr { + uint32_t val; + struct { + uint16_t rsvd__16_31; + uint16_t exti; + } bit; +}; + +/* 7.2 SYSCFG registers */ +struct stm32f4x_syscfg { + uint32_t memrmp; + uint32_t pmc; + union syscfg_exticr exticr1; + union syscfg_exticr exticr2; + union syscfg_exticr exticr3; + union syscfg_exticr exticr4; + uint32_t cmpcr; +}; + +#endif /* _STM32F4X_GPIO_REGISTERS_H_ */ diff --git a/arch/arm/soc/st_stm32/stm32f4/soc.h b/arch/arm/soc/st_stm32/stm32f4/soc.h index e58307b939f..53685ca2803 100644 --- a/arch/arm/soc/st_stm32/stm32f4/soc.h +++ b/arch/arm/soc/st_stm32/stm32f4/soc.h @@ -68,12 +68,35 @@ /* FLASH */ #define FLASH_R_BASE (AHB1PERIPH_BASE + 0x3C00) +/* SYSCFG */ +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x3800) + #ifndef _ASMLANGUAGE #include #include #include +/* IO pin functions */ +enum stm32f4x_pin_config_mode { + STM32F4X_PIN_CONFIG_DRIVE_PUSH_PULL, + STM32F4X_PIN_CONFIG_DRIVE_PUSH_UP, + STM32F4X_PIN_CONFIG_DRIVE_PUSH_DOWN, + STM32F4X_PIN_CONFIG_DRIVE_OPEN_DRAIN, + STM32F4X_PIN_CONFIG_DRIVE_OPEN_UP, + STM32F4X_PIN_CONFIG_DRIVE_OPEN_DOWN, + STM32F4X_PIN_CONFIG_AF_PUSH_PULL, + STM32F4X_PIN_CONFIG_AF_PUSH_UP, + STM32F4X_PIN_CONFIG_AF_PUSH_DOWN, + STM32F4X_PIN_CONFIG_AF_OPEN_DRAIN, + STM32F4X_PIN_CONFIG_AF_OPEN_UP, + STM32F4X_PIN_CONFIG_AF_OPEN_DOWN, + STM32F4X_PIN_CONFIG_BIAS_HIGH_IMPEDANCE, + STM32F4X_PIN_CONFIG_BIAS_PULL_UP, + STM32F4X_PIN_CONFIG_BIAS_PULL_DOWN, + STM32F4X_PIN_CONFIG_ANALOG, +}; + #include "soc_irq.h" #endif /* !_ASMLANGUAGE */ diff --git a/arch/arm/soc/st_stm32/stm32f4/soc_gpio.c b/arch/arm/soc/st_stm32/stm32f4/soc_gpio.c new file mode 100644 index 00000000000..f5918d994e7 --- /dev/null +++ b/arch/arm/soc/st_stm32/stm32f4/soc_gpio.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) Linaro Limited. + * + * 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 + * + * Based on reference manual: + * RM0368 Reference manual STM32F401xB/C and STM32F401xD/E + * advanced ARM ® -based 32-bit MCUs + * + * Chapter 8: General-purpose I/Os (GPIOs) + */ + +#include + +#include +#include "soc.h" +#include "soc_registers.h" +#include +#include + +/** + * @brief map pin function to MODE register value + */ +static uint32_t __func_to_mode(int func) +{ + switch (func) { + case STM32F4X_PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + case STM32F4X_PIN_CONFIG_BIAS_PULL_UP: + case STM32F4X_PIN_CONFIG_BIAS_PULL_DOWN: + return 0x0; + case STM32F4X_PIN_CONFIG_DRIVE_PUSH_PULL: + case STM32F4X_PIN_CONFIG_DRIVE_PUSH_UP: + case STM32F4X_PIN_CONFIG_DRIVE_PUSH_DOWN: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DRAIN: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_UP: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DOWN: + return 0x1; + case STM32F4X_PIN_CONFIG_AF_PUSH_PULL: + case STM32F4X_PIN_CONFIG_AF_PUSH_UP: + case STM32F4X_PIN_CONFIG_AF_PUSH_DOWN: + case STM32F4X_PIN_CONFIG_AF_OPEN_DRAIN: + case STM32F4X_PIN_CONFIG_AF_OPEN_UP: + case STM32F4X_PIN_CONFIG_AF_OPEN_DOWN: + return 0x2; + case STM32F4X_PIN_CONFIG_ANALOG: + return 0x3; + } + + return 0; +} + +/** + * @brief map pin function to OTYPE register value + */ +static uint32_t __func_to_otype(int func) +{ + switch (func) { + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DRAIN: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_UP: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DOWN: + case STM32F4X_PIN_CONFIG_AF_OPEN_DRAIN: + case STM32F4X_PIN_CONFIG_AF_OPEN_UP: + case STM32F4X_PIN_CONFIG_AF_OPEN_DOWN: + return 0x1; + } + + return 0; +} + +/** + * @brief map pin function to OSPEED register value + */ +static uint32_t __func_to_ospeed(int func) +{ + switch (func) { + case STM32F4X_PIN_CONFIG_DRIVE_PUSH_PULL: + case STM32F4X_PIN_CONFIG_DRIVE_PUSH_UP: + case STM32F4X_PIN_CONFIG_DRIVE_PUSH_DOWN: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DRAIN: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_UP: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DOWN: + case STM32F4X_PIN_CONFIG_AF_PUSH_PULL: + case STM32F4X_PIN_CONFIG_AF_PUSH_UP: + case STM32F4X_PIN_CONFIG_AF_PUSH_DOWN: + case STM32F4X_PIN_CONFIG_AF_OPEN_DRAIN: + case STM32F4X_PIN_CONFIG_AF_OPEN_UP: + case STM32F4X_PIN_CONFIG_AF_OPEN_DOWN: + /* Force fast speed by default */ + return 0x2; + } + + return 0; +} + +/** + * @brief map pin function to PUPD register value + */ +static uint32_t __func_to_pupd(int func) +{ + switch (func) { + case STM32F4X_PIN_CONFIG_DRIVE_PUSH_PULL: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DRAIN: + case STM32F4X_PIN_CONFIG_AF_PUSH_PULL: + case STM32F4X_PIN_CONFIG_AF_OPEN_DRAIN: + case STM32F4X_PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + case STM32F4X_PIN_CONFIG_ANALOG: + return 0x0; + case STM32F4X_PIN_CONFIG_DRIVE_PUSH_UP: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_UP: + case STM32F4X_PIN_CONFIG_AF_PUSH_UP: + case STM32F4X_PIN_CONFIG_AF_OPEN_UP: + case STM32F4X_PIN_CONFIG_BIAS_PULL_UP: + return 0x1; + case STM32F4X_PIN_CONFIG_DRIVE_PUSH_DOWN: + case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DOWN: + case STM32F4X_PIN_CONFIG_AF_PUSH_DOWN: + case STM32F4X_PIN_CONFIG_AF_OPEN_DOWN: + case STM32F4X_PIN_CONFIG_BIAS_PULL_DOWN: + return 0x2; + } + + return 0; +} + + +int stm32_gpio_flags_to_conf(int flags, int *pincfg) +{ + int direction = flags & GPIO_DIR_MASK; + int pud = flags & GPIO_PUD_MASK; + + if (!pincfg) { + return -EINVAL; + } + + if (direction == GPIO_DIR_OUT) { + if (pud == GPIO_PUD_PULL_UP) { + *pincfg = STM32F4X_PIN_CONFIG_DRIVE_PUSH_UP; + } else if (pud == GPIO_PUD_PULL_DOWN) { + *pincfg = STM32F4X_PIN_CONFIG_DRIVE_PUSH_DOWN; + } else { + *pincfg = STM32F4X_PIN_CONFIG_DRIVE_PUSH_PULL; + } + } else if (direction == GPIO_DIR_IN) { + if (pud == GPIO_PUD_PULL_UP) { + *pincfg = STM32F4X_PIN_CONFIG_BIAS_PULL_UP; + } else if (pud == GPIO_PUD_PULL_DOWN) { + *pincfg = STM32F4X_PIN_CONFIG_BIAS_PULL_DOWN; + } else { + *pincfg = STM32F4X_PIN_CONFIG_BIAS_HIGH_IMPEDANCE; + } + } else { + return -ENOTSUP; + } + + return 0; +} + +int stm32_gpio_configure(uint32_t *base_addr, int pin, int conf, int altf) +{ + volatile struct stm32f4x_gpio *gpio = + (struct stm32f4x_gpio *)(base_addr); + uint32_t mode = __func_to_mode(conf); + uint32_t otype = __func_to_otype(conf); + uint32_t ospeed = __func_to_ospeed(conf); + uint32_t pupd = __func_to_pupd(conf); + uint32_t tmpreg = 0; + + /* TODO: validate if indeed alternate */ + if (altf) { + /* Set the alternate function */ + tmpreg = gpio->afr[pin >> 0x3]; + tmpreg &= ~(0xf << ((pin & 0x07) * 4)); + tmpreg |= (altf << ((pin & 0x07) * 4)); + gpio->afr[pin >> 0x3] = tmpreg; + } + + /* Set the IO direction mode */ + tmpreg = gpio->mode; + tmpreg &= ~(0x3 << (pin * 2)); + tmpreg |= (mode << (pin * 2)); + gpio->mode = tmpreg; + + if (otype) { + tmpreg = gpio->otype; + tmpreg &= ~(0x1 << pin); + tmpreg |= (otype << pin); + gpio->otype = tmpreg; + } + + if (ospeed) { + tmpreg = gpio->ospeed; + tmpreg &= ~(0x3 << (pin * 2)); + tmpreg |= (ospeed << (pin * 2)); + gpio->ospeed = tmpreg; + } + + tmpreg = gpio->pupdr; + tmpreg &= ~(0x3 << (pin * 2)); + tmpreg |= (pupd << (pin * 2)); + gpio->pupdr = tmpreg; + + return 0; +} + +int stm32_gpio_set(uint32_t *base, int pin, int value) +{ + struct stm32f4x_gpio *gpio = (struct stm32f4x_gpio *)base; + + int pval = 1 << (pin & 0xf); + + if (value) { + gpio->odr |= pval; + } else { + gpio->odr &= ~pval; + } + + return 0; +} + +int stm32_gpio_get(uint32_t *base, int pin) +{ + struct stm32f4x_gpio *gpio = (struct stm32f4x_gpio *)base; + + return (gpio->idr >> pin) & 0x1; +} + +int stm32_gpio_enable_int(int port, int pin) +{ + volatile struct stm32f4x_syscfg *syscfg = + (struct stm32f4x_syscfg *)SYSCFG_BASE; + volatile union syscfg_exticr *exticr; + struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME); + struct stm32f4x_pclken pclken = { + .bus = STM32F4X_CLOCK_BUS_APB2, + .enr = STM32F4X_CLOCK_ENABLE_SYSCFG + }; + int shift = 0; + + /* Enable SYSCFG clock */ + clock_control_on(clk, (clock_control_subsys_t *) &pclken); + + if (pin <= 3) { + exticr = &syscfg->exticr1; + } else if (pin <= 7) { + exticr = &syscfg->exticr2; + } else if (pin <= 11) { + exticr = &syscfg->exticr3; + } else if (pin <= 15) { + exticr = &syscfg->exticr4; + } else { + return -EINVAL; + } + + shift = 4 * (pin % 4); + + exticr->val &= ~(0xf << shift); + exticr->val |= port << shift; + + return 0; +} diff --git a/arch/arm/soc/st_stm32/stm32f4/soc_registers.h b/arch/arm/soc/st_stm32/stm32f4/soc_registers.h index 0453574deae..4fc2ea7b6ad 100644 --- a/arch/arm/soc/st_stm32/stm32f4/soc_registers.h +++ b/arch/arm/soc/st_stm32/stm32f4/soc_registers.h @@ -20,5 +20,6 @@ /* include register mapping headers */ #include "rcc_registers.h" #include "flash_registers.h" +#include "gpio_registers.h" #endif /* _STM32F4_SOC_REGISTERS_H_ */ diff --git a/drivers/gpio/gpio_stm32.c b/drivers/gpio/gpio_stm32.c index 8af46aba751..c8a720a953a 100644 --- a/drivers/gpio/gpio_stm32.c +++ b/drivers/gpio/gpio_stm32.c @@ -198,11 +198,18 @@ static int gpio_stm32_init(struct device *device) struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME); +#ifdef CONFIG_SOC_SERIES_STM32F1X clock_control_on(clk, cfg->clock_subsys); +#elif CONFIG_SOC_SERIES_STM32F4X + clock_control_on(clk, (clock_control_subsys_t *) &cfg->pclken); +#endif return 0; } +#ifdef CONFIG_SOC_SERIES_STM32F1X + +/* TODO: Change F1 to work similarly to F4 */ #define GPIO_DEVICE_INIT(__name, __suffix, __base_addr, __port, __clock) \ static const struct gpio_stm32_config gpio_stm32_cfg_## __suffix = { \ .base = (uint32_t *)__base_addr, \ @@ -219,11 +226,32 @@ DEVICE_AND_API_INIT(gpio_stm32_## __suffix, \ CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ &gpio_stm32_driver); +#elif CONFIG_SOC_SERIES_STM32F4X + +#define GPIO_DEVICE_INIT(__name, __suffix, __base_addr, __port, __cenr) \ +static const struct gpio_stm32_config gpio_stm32_cfg_## __suffix = { \ + .base = (uint32_t *)__base_addr, \ + .port = __port, \ + .pclken = { .bus = STM32F4X_CLOCK_BUS_AHB1, .enr = __cenr }, \ +}; \ +static struct gpio_stm32_data gpio_stm32_data_## __suffix; \ +DEVICE_AND_API_INIT(gpio_stm32_## __suffix, \ + __name, \ + gpio_stm32_init, \ + &gpio_stm32_data_## __suffix, \ + &gpio_stm32_cfg_## __suffix, \ + SECONDARY, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &gpio_stm32_driver); +#endif + #ifdef CONFIG_GPIO_STM32_PORTA GPIO_DEVICE_INIT("GPIOA", a, GPIOA_BASE, STM32_PORTA, #ifdef CONFIG_SOC_SERIES_STM32F1X STM32F10X_CLOCK_SUBSYS_IOPA | STM32F10X_CLOCK_SUBSYS_AFIO +#elif CONFIG_SOC_SERIES_STM32F4X + STM32F4X_CLOCK_ENABLE_GPIOA #endif ); #endif /* CONFIG_GPIO_STM32_PORTA */ @@ -233,6 +261,8 @@ GPIO_DEVICE_INIT("GPIOB", b, GPIOB_BASE, STM32_PORTB, #ifdef CONFIG_SOC_SERIES_STM32F1X STM32F10X_CLOCK_SUBSYS_IOPB | STM32F10X_CLOCK_SUBSYS_AFIO +#elif CONFIG_SOC_SERIES_STM32F4X + STM32F4X_CLOCK_ENABLE_GPIOB #endif ); #endif /* CONFIG_GPIO_STM32_PORTB */ @@ -242,6 +272,8 @@ GPIO_DEVICE_INIT("GPIOC", c, GPIOC_BASE, STM32_PORTC, #ifdef CONFIG_SOC_SERIES_STM32F1X STM32F10X_CLOCK_SUBSYS_IOPC | STM32F10X_CLOCK_SUBSYS_AFIO +#elif CONFIG_SOC_SERIES_STM32F4X + STM32F4X_CLOCK_ENABLE_GPIOC #endif ); #endif /* CONFIG_GPIO_STM32_PORTC */ @@ -251,6 +283,8 @@ GPIO_DEVICE_INIT("GPIOD", d, GPIOD_BASE, STM32_PORTD, #ifdef CONFIG_SOC_SERIES_STM32F1X STM32F10X_CLOCK_SUBSYS_IOPD | STM32F10X_CLOCK_SUBSYS_AFIO +#elif CONFIG_SOC_SERIES_STM32F4X + STM32F4X_CLOCK_ENABLE_GPIOD #endif ); #endif /* CONFIG_GPIO_STM32_PORTD */ @@ -260,6 +294,8 @@ GPIO_DEVICE_INIT("GPIOE", e, GPIOE_BASE, STM32_PORTE, #ifdef CONFIG_SOC_SERIES_STM32F1X STM32F10X_CLOCK_SUBSYS_IOPE | STM32F10X_CLOCK_SUBSYS_AFIO +#elif CONFIG_SOC_SERIES_STM32F4X + STM32F4X_CLOCK_ENABLE_GPIOE #endif ); #endif /* CONFIG_GPIO_STM32_PORTE */ diff --git a/drivers/gpio/gpio_stm32.h b/drivers/gpio/gpio_stm32.h index 51dc847fd7e..29fa1684031 100644 --- a/drivers/gpio/gpio_stm32.h +++ b/drivers/gpio/gpio_stm32.h @@ -34,8 +34,12 @@ struct gpio_stm32_config { uint32_t *base; /* IO port */ enum stm32_pin_port port; +#ifdef CONFIG_SOC_SERIES_STM32F1X /* clock subsystem */ clock_control_subsys_t clock_subsys; +#elif CONFIG_SOC_SERIES_STM32F4X + struct stm32f4x_pclken pclken; +#endif }; /**