mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-30 16:55:22 +00:00
It needs to verify if the callback was not already installed, and if so: if is was in controller's list. It should return an error in case the node is not found though it was requested to be removed. If already inserted, it will be silently removed but added again, to avoid circular list as stated in the bug. Fixes #11394 Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
485 lines
11 KiB
C
485 lines
11 KiB
C
/*
|
|
* Copyright (c) 2016 Open-RnD Sp. z o.o.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include <kernel.h>
|
|
#include <device.h>
|
|
#include <soc.h>
|
|
#include <gpio.h>
|
|
#include <clock_control/stm32_clock_control.h>
|
|
#include <pinmux/stm32/pinmux_stm32.h>
|
|
#include <pinmux.h>
|
|
#include <misc/util.h>
|
|
#include <interrupt_controller/exti_stm32.h>
|
|
|
|
#include "gpio_stm32.h"
|
|
#include "gpio_utils.h"
|
|
|
|
/**
|
|
* @brief Common GPIO driver for STM32 MCUs.
|
|
*/
|
|
|
|
/**
|
|
* @brief EXTI interrupt callback
|
|
*/
|
|
static void gpio_stm32_isr(int line, void *arg)
|
|
{
|
|
struct device *dev = arg;
|
|
struct gpio_stm32_data *data = dev->driver_data;
|
|
|
|
if ((BIT(line) & data->cb_pins) != 0) {
|
|
_gpio_fire_callbacks(&data->cb, dev, BIT(line));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Common gpio flags to custom flags
|
|
*/
|
|
const int gpio_stm32_flags_to_conf(int flags, int *pincfg)
|
|
{
|
|
int direction = flags & GPIO_DIR_MASK;
|
|
int pud = flags & GPIO_PUD_MASK;
|
|
|
|
if (pincfg == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (direction == GPIO_DIR_OUT) {
|
|
*pincfg = STM32_PINCFG_MODE_OUTPUT;
|
|
} else {
|
|
/* pull-{up,down} maybe? */
|
|
*pincfg = STM32_PINCFG_MODE_INPUT;
|
|
if (pud == GPIO_PUD_PULL_UP) {
|
|
*pincfg |= STM32_PINCFG_PULL_UP;
|
|
} else if (pud == GPIO_PUD_PULL_DOWN) {
|
|
*pincfg |= STM32_PINCFG_PULL_DOWN;
|
|
} else {
|
|
/* floating */
|
|
*pincfg |= STM32_PINCFG_FLOATING;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Translate pin to pinval that the LL library needs
|
|
*/
|
|
static inline u32_t stm32_pinval_get(int pin)
|
|
{
|
|
u32_t pinval;
|
|
|
|
#ifdef CONFIG_SOC_SERIES_STM32F1X
|
|
pinval = (1 << pin) << GPIO_PIN_MASK_POS;
|
|
if (pin < 8) {
|
|
pinval |= 1 << pin;
|
|
} else {
|
|
pinval |= (1 << (pin % 8)) | 0x04000000;
|
|
}
|
|
#else
|
|
pinval = 1 << pin;
|
|
#endif
|
|
return pinval;
|
|
}
|
|
|
|
/**
|
|
* @brief Configure the hardware.
|
|
*/
|
|
int gpio_stm32_configure(u32_t *base_addr, int pin, int conf, int altf)
|
|
{
|
|
GPIO_TypeDef *gpio = (GPIO_TypeDef *)base_addr;
|
|
|
|
int pin_ll = stm32_pinval_get(pin);
|
|
#ifdef CONFIG_SOC_SERIES_STM32F1X
|
|
ARG_UNUSED(altf);
|
|
|
|
u32_t temp = conf & (STM32_MODE_INOUT_MASK << STM32_MODE_INOUT_SHIFT);
|
|
|
|
if (temp == STM32_MODE_INPUT) {
|
|
temp = conf & (STM32_CNF_IN_MASK << STM32_CNF_IN_SHIFT);
|
|
|
|
if (temp == STM32_CNF_IN_ANALOG) {
|
|
LL_GPIO_SetPinMode(gpio, pin_ll, LL_GPIO_MODE_ANALOG);
|
|
} else if (temp == STM32_CNF_IN_FLOAT) {
|
|
LL_GPIO_SetPinMode(gpio, pin_ll, LL_GPIO_MODE_FLOATING);
|
|
} else {
|
|
LL_GPIO_SetPinMode(gpio, pin_ll, LL_GPIO_MODE_INPUT);
|
|
|
|
temp = conf & (STM32_PUPD_MASK << STM32_PUPD_SHIFT);
|
|
|
|
if (temp == STM32_PUPD_PULL_UP) {
|
|
LL_GPIO_SetPinPull(gpio, pin_ll, LL_GPIO_PULL_UP);
|
|
} else {
|
|
LL_GPIO_SetPinPull(gpio, pin_ll, LL_GPIO_PULL_DOWN);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
temp = conf & (STM32_CNF_OUT_1_MASK << STM32_CNF_OUT_1_SHIFT);
|
|
|
|
if (temp == STM32_CNF_GP_OUTPUT) {
|
|
LL_GPIO_SetPinMode(gpio, pin_ll, LL_GPIO_MODE_OUTPUT);
|
|
} else {
|
|
LL_GPIO_SetPinMode(gpio, pin_ll, LL_GPIO_MODE_ALTERNATE);
|
|
}
|
|
|
|
temp = conf & (STM32_CNF_OUT_0_MASK << STM32_CNF_OUT_0_SHIFT);
|
|
|
|
if (temp == STM32_CNF_PUSH_PULL) {
|
|
LL_GPIO_SetPinOutputType(gpio, pin_ll, LL_GPIO_OUTPUT_PUSHPULL);
|
|
} else {
|
|
LL_GPIO_SetPinOutputType(gpio, pin_ll, LL_GPIO_OUTPUT_OPENDRAIN);
|
|
}
|
|
|
|
temp = conf & (STM32_MODE_OSPEED_MASK << STM32_MODE_OSPEED_SHIFT);
|
|
|
|
if (temp == STM32_MODE_OUTPUT_MAX_2) {
|
|
LL_GPIO_SetPinSpeed(gpio, pin_ll, LL_GPIO_SPEED_FREQ_LOW);
|
|
} else if (temp == STM32_MODE_OUTPUT_MAX_10) {
|
|
LL_GPIO_SetPinSpeed(gpio, pin_ll, LL_GPIO_SPEED_FREQ_MEDIUM);
|
|
} else {
|
|
LL_GPIO_SetPinSpeed(gpio, pin_ll, LL_GPIO_SPEED_FREQ_HIGH);
|
|
}
|
|
}
|
|
#else
|
|
unsigned int mode, otype, ospeed, pupd;
|
|
|
|
mode = conf & (STM32_MODER_MASK << STM32_MODER_SHIFT);
|
|
otype = conf & (STM32_OTYPER_MASK << STM32_OTYPER_SHIFT);
|
|
ospeed = conf & (STM32_OSPEEDR_MASK << STM32_OSPEEDR_SHIFT);
|
|
pupd = conf & (STM32_PUPDR_MASK << STM32_PUPDR_SHIFT);
|
|
|
|
LL_GPIO_SetPinMode(gpio, pin_ll, mode >> STM32_MODER_SHIFT);
|
|
|
|
if (STM32_MODER_ALT_MODE == mode) {
|
|
if (pin < 8) {
|
|
LL_GPIO_SetAFPin_0_7(gpio, pin_ll, altf);
|
|
} else {
|
|
LL_GPIO_SetAFPin_8_15(gpio, pin_ll, altf);
|
|
}
|
|
}
|
|
|
|
LL_GPIO_SetPinOutputType(gpio, pin_ll, otype >> STM32_OTYPER_SHIFT);
|
|
|
|
LL_GPIO_SetPinSpeed(gpio, pin_ll, ospeed >> STM32_OSPEEDR_SHIFT);
|
|
|
|
LL_GPIO_SetPinPull(gpio, pin_ll, pupd >> STM32_PUPDR_SHIFT);
|
|
|
|
#endif /* CONFIG_SOC_SERIES_STM32F1X */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Enable EXTI of the specific line
|
|
*/
|
|
const int gpio_stm32_enable_int(int port, int pin)
|
|
{
|
|
#if defined(CONFIG_SOC_SERIES_STM32F2X) || \
|
|
defined(CONFIG_SOC_SERIES_STM32F3X) || \
|
|
defined(CONFIG_SOC_SERIES_STM32F4X) || \
|
|
defined(CONFIG_SOC_SERIES_STM32F7X) || \
|
|
defined(CONFIG_SOC_SERIES_STM32L4X)
|
|
struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
|
|
struct stm32_pclken pclken = {
|
|
.bus = STM32_CLOCK_BUS_APB2,
|
|
.enr = LL_APB2_GRP1_PERIPH_SYSCFG
|
|
};
|
|
/* Enable SYSCFG clock */
|
|
clock_control_on(clk, (clock_control_subsys_t *) &pclken);
|
|
#endif
|
|
|
|
uint32_t line;
|
|
|
|
if (pin > 15) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
#if defined(CONFIG_SOC_SERIES_STM32L0X) || \
|
|
defined(CONFIG_SOC_SERIES_STM32F0X)
|
|
line = ((pin % 4 * 4) << 16) | (pin / 4);
|
|
#else
|
|
line = (0xF << ((pin % 4 * 4) + 16)) | (pin / 4);
|
|
#endif
|
|
|
|
#ifdef CONFIG_SOC_SERIES_STM32L0X
|
|
/*
|
|
* Ports F and G are not present on some STM32L0 parts, so
|
|
* for these parts port H external interrupt should be enabled
|
|
* by writing value 0x5 instead of 0x7.
|
|
*/
|
|
if (port == STM32_PORTH) {
|
|
port = LL_SYSCFG_EXTI_PORTH;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_SOC_SERIES_STM32F1X
|
|
LL_GPIO_AF_SetEXTISource(port, line);
|
|
#else
|
|
LL_SYSCFG_SetEXTISource(port, line);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Configure pin or port
|
|
*/
|
|
static int gpio_stm32_config(struct device *dev, int access_op,
|
|
u32_t pin, int flags)
|
|
{
|
|
const struct gpio_stm32_config *cfg = dev->config->config_info;
|
|
int pincfg;
|
|
int map_res;
|
|
|
|
if (access_op != GPIO_ACCESS_BY_PIN) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* figure out if we can map the requested GPIO
|
|
* configuration
|
|
*/
|
|
map_res = gpio_stm32_flags_to_conf(flags, &pincfg);
|
|
if (map_res != 0) {
|
|
return map_res;
|
|
}
|
|
|
|
if (gpio_stm32_configure(cfg->base, pin, pincfg, 0) != 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
if ((flags & GPIO_INT) != 0) {
|
|
|
|
if (stm32_exti_set_callback(pin, cfg->port,
|
|
gpio_stm32_isr, dev) != 0) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
gpio_stm32_enable_int(cfg->port, pin);
|
|
|
|
if ((flags & GPIO_INT_EDGE) != 0) {
|
|
int edge = 0;
|
|
|
|
if ((flags & GPIO_INT_DOUBLE_EDGE) != 0) {
|
|
edge = STM32_EXTI_TRIG_RISING |
|
|
STM32_EXTI_TRIG_FALLING;
|
|
} else if ((flags & GPIO_INT_ACTIVE_HIGH) != 0) {
|
|
edge = STM32_EXTI_TRIG_RISING;
|
|
} else {
|
|
edge = STM32_EXTI_TRIG_FALLING;
|
|
}
|
|
|
|
stm32_exti_trigger(pin, edge);
|
|
}
|
|
|
|
if (stm32_exti_enable(pin) != 0) {
|
|
return -ENOSYS;
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the pin or port output
|
|
*/
|
|
static int gpio_stm32_write(struct device *dev, int access_op,
|
|
u32_t pin, u32_t value)
|
|
{
|
|
const struct gpio_stm32_config *cfg = dev->config->config_info;
|
|
GPIO_TypeDef *gpio = (GPIO_TypeDef *)cfg->base;
|
|
|
|
if (access_op != GPIO_ACCESS_BY_PIN) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
pin = stm32_pinval_get(pin);
|
|
if (value != 0) {
|
|
LL_GPIO_SetOutputPin(gpio, pin);
|
|
} else {
|
|
LL_GPIO_ResetOutputPin(gpio, pin);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Read the pin or port status
|
|
*/
|
|
static int gpio_stm32_read(struct device *dev, int access_op,
|
|
u32_t pin, u32_t *value)
|
|
{
|
|
const struct gpio_stm32_config *cfg = dev->config->config_info;
|
|
GPIO_TypeDef *gpio = (GPIO_TypeDef *)cfg->base;
|
|
|
|
if (access_op != GPIO_ACCESS_BY_PIN) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
*value = (LL_GPIO_ReadInputPort(gpio) >> pin) & 0x1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_stm32_manage_callback(struct device *dev,
|
|
struct gpio_callback *callback,
|
|
bool set)
|
|
{
|
|
struct gpio_stm32_data *data = dev->driver_data;
|
|
|
|
return _gpio_manage_callback(&data->cb, callback, set);
|
|
}
|
|
|
|
static int gpio_stm32_enable_callback(struct device *dev,
|
|
int access_op, u32_t pin)
|
|
{
|
|
struct gpio_stm32_data *data = dev->driver_data;
|
|
|
|
if (access_op != GPIO_ACCESS_BY_PIN) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
data->cb_pins |= BIT(pin);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_stm32_disable_callback(struct device *dev,
|
|
int access_op, u32_t pin)
|
|
{
|
|
struct gpio_stm32_data *data = dev->driver_data;
|
|
|
|
if (access_op != GPIO_ACCESS_BY_PIN) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
data->cb_pins &= ~BIT(pin);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct gpio_driver_api gpio_stm32_driver = {
|
|
.config = gpio_stm32_config,
|
|
.write = gpio_stm32_write,
|
|
.read = gpio_stm32_read,
|
|
.manage_callback = gpio_stm32_manage_callback,
|
|
.enable_callback = gpio_stm32_enable_callback,
|
|
.disable_callback = gpio_stm32_disable_callback,
|
|
|
|
};
|
|
|
|
/**
|
|
* @brief Initialize GPIO port
|
|
*
|
|
* Perform basic initialization of a GPIO port. The code will
|
|
* enable the clock for corresponding peripheral.
|
|
*
|
|
* @param dev GPIO device struct
|
|
*
|
|
* @return 0
|
|
*/
|
|
static int gpio_stm32_init(struct device *device)
|
|
{
|
|
const struct gpio_stm32_config *cfg = device->config->config_info;
|
|
|
|
/* enable clock for subsystem */
|
|
struct device *clk =
|
|
device_get_binding(STM32_CLOCK_CONTROL_NAME);
|
|
|
|
if (clock_control_on(clk,
|
|
(clock_control_subsys_t *) &cfg->pclken) != 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
#ifdef PWR_CR2_IOSV
|
|
if (cfg->port == STM32_PORTG) {
|
|
/* Port G[15:2] requires external power supply */
|
|
/* Cf: L4XX RM, §5.1 Power supplies */
|
|
if (LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_PWR)) {
|
|
LL_PWR_EnableVddIO2();
|
|
} else {
|
|
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
|
|
LL_PWR_EnableVddIO2();
|
|
LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_PWR);
|
|
}
|
|
}
|
|
#endif /* PWR_CR2_IOSV */
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define GPIO_DEVICE_INIT(__name, __suffix, __base_addr, __port, __cenr, __bus) \
|
|
static const struct gpio_stm32_config gpio_stm32_cfg_## __suffix = { \
|
|
.base = (u32_t *)__base_addr, \
|
|
.port = __port, \
|
|
.pclken = { .bus = __bus, .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, \
|
|
POST_KERNEL, \
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
|
&gpio_stm32_driver);
|
|
|
|
|
|
#define GPIO_DEVICE_INIT_STM32(__suffix, __SUFFIX) \
|
|
GPIO_DEVICE_INIT(DT_GPIO_STM32_GPIO##__SUFFIX##_LABEL, \
|
|
__suffix, \
|
|
DT_GPIO_STM32_GPIO##__SUFFIX##_BASE_ADDRESS, \
|
|
STM32_PORT##__SUFFIX, \
|
|
DT_GPIO_STM32_GPIO##__SUFFIX##_CLOCK_BITS, \
|
|
DT_GPIO_STM32_GPIO##__SUFFIX##_CLOCK_BUS)
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTA
|
|
GPIO_DEVICE_INIT_STM32(a, A);
|
|
#endif /* CONFIG_GPIO_STM32_PORTA */
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTB
|
|
GPIO_DEVICE_INIT_STM32(b, B);
|
|
#endif /* CONFIG_GPIO_STM32_PORTB */
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTC
|
|
GPIO_DEVICE_INIT_STM32(c, C);
|
|
#endif /* CONFIG_GPIO_STM32_PORTC */
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTD
|
|
GPIO_DEVICE_INIT_STM32(d, D);
|
|
#endif /* CONFIG_GPIO_STM32_PORTD */
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTE
|
|
GPIO_DEVICE_INIT_STM32(e, E);
|
|
#endif /* CONFIG_GPIO_STM32_PORTE */
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTF
|
|
GPIO_DEVICE_INIT_STM32(f, F);
|
|
#endif /* CONFIG_GPIO_STM32_PORTF */
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTG
|
|
GPIO_DEVICE_INIT_STM32(g, G);
|
|
#endif /* CONFIG_GPIO_STM32_PORTG */
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTH
|
|
GPIO_DEVICE_INIT_STM32(h, H);
|
|
#endif /* CONFIG_GPIO_STM32_PORTH */
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTI
|
|
GPIO_DEVICE_INIT_STM32(i, I);
|
|
#endif /* CONFIG_GPIO_STM32_PORTI */
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTJ
|
|
GPIO_DEVICE_INIT_STM32(j, J);
|
|
#endif /* CONFIG_GPIO_STM32_PORTJ */
|
|
|
|
#ifdef CONFIG_GPIO_STM32_PORTK
|
|
GPIO_DEVICE_INIT_STM32(k, K);
|
|
#endif /* CONFIG_GPIO_STM32_PORTK */
|