zephyr/drivers/gpio/gpio_esp32.c
Stephanos Ioannidis 11d27f12ad drivers: gpio_esp32: Drop IRAM_ATTR from forward declarations
This commit drops the `IRAM_ATTR` macro from the function declarations
because:

1. `IRAM_ATTR` macro makes use of the `__COUNTER__` preprocessor macro,
   which increments for every macro invocation and causes the section
   specified in the forward declaration to not match that of the
   function definition.

2. Section attributes need not be specified for forward declarations.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
2022-06-20 10:28:06 +02:00

483 lines
12 KiB
C

/*
* Copyright (c) 2017 Intel Corporation
* Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT espressif_esp32_gpio
/* Include esp-idf headers first to avoid redefining BIT() macro */
#include <soc/gpio_reg.h>
#include <soc/io_mux_reg.h>
#include <soc/soc.h>
#include <hal/gpio_ll.h>
#include <esp_attr.h>
#include <hal/rtc_io_hal.h>
#include <soc.h>
#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/dt-bindings/gpio/espressif-esp32-gpio.h>
#ifdef CONFIG_SOC_ESP32C3
#include <zephyr/drivers/interrupt_controller/intc_esp32c3.h>
#else
#include <zephyr/drivers/interrupt_controller/intc_esp32.h>
#endif
#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>
#include "gpio_utils.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(gpio_esp32, CONFIG_LOG_DEFAULT_LEVEL);
#ifdef CONFIG_SOC_ESP32C3
/* gpio structs in esp32c3 series are different from xtensa ones */
#define out out.data
#define in in.data
#define out_w1ts out_w1ts.val
#define out_w1tc out_w1tc.val
/* arch_curr_cpu() is not available for riscv based chips */
#define CPU_ID() 0
#define ISR_HANDLER isr_handler_t
#else
#define CPU_ID() arch_curr_cpu()->id
#define ISR_HANDLER intr_handler_t
#endif
#ifndef SOC_GPIO_SUPPORT_RTC_INDEPENDENT
#define SOC_GPIO_SUPPORT_RTC_INDEPENDENT 0
#endif
struct gpio_esp32_config {
/* gpio_driver_config needs to be first */
struct gpio_driver_config drv_cfg;
gpio_dev_t *const gpio_base;
gpio_dev_t *const gpio_dev;
const int gpio_port;
};
struct gpio_esp32_data {
/* gpio_driver_data needs to be first */
struct gpio_driver_data common;
sys_slist_t cb;
};
static inline bool rtc_gpio_is_valid_gpio(uint32_t gpio_num)
{
#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
return (gpio_num < SOC_GPIO_PIN_COUNT && rtc_io_num_map[gpio_num] >= 0);
#else
return false;
#endif
}
static inline bool gpio_pin_is_valid(uint32_t pin)
{
return ((BIT(pin) & SOC_GPIO_VALID_GPIO_MASK) != 0);
}
static inline bool gpio_pin_is_output_capable(uint32_t pin)
{
return ((BIT(pin) & SOC_GPIO_VALID_OUTPUT_GPIO_MASK) != 0);
}
static int gpio_esp32_config(const struct device *dev,
gpio_pin_t pin,
gpio_flags_t flags)
{
const struct gpio_esp32_config *const cfg = dev->config;
struct gpio_esp32_data *data = dev->data;
uint32_t io_pin = (uint32_t) pin + ((cfg->gpio_port == 1 && pin < 32) ? 32 : 0);
uint32_t key;
int ret = 0;
if (!gpio_pin_is_valid(io_pin)) {
LOG_ERR("Selected IO pin is not valid.");
return -EINVAL;
}
key = irq_lock();
#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
if (rtc_gpio_is_valid_gpio(io_pin)) {
rtcio_hal_function_select(rtc_io_num_map[io_pin], RTCIO_FUNC_DIGITAL);
}
#endif
if (io_pin >= GPIO_NUM_MAX) {
LOG_ERR("Invalid pin.");
ret = -EINVAL;
goto end;
}
/* Set pin function as GPIO */
gpio_ll_iomux_func_sel(GPIO_PIN_MUX_REG[io_pin], PIN_FUNC_GPIO);
if (flags & GPIO_PULL_UP) {
if (!rtc_gpio_is_valid_gpio(io_pin) || SOC_GPIO_SUPPORT_RTC_INDEPENDENT) {
gpio_ll_pulldown_dis(&GPIO, io_pin);
gpio_ll_pullup_en(&GPIO, io_pin);
} else {
#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
int rtcio_num = rtc_io_num_map[io_pin];
rtcio_hal_pulldown_disable(rtc_io_num_map[io_pin]);
if (rtc_io_desc[rtcio_num].pullup) {
rtcio_hal_pullup_enable(rtc_io_num_map[io_pin]);
} else {
ret = -ENOTSUP;
goto end;
}
#endif
}
} else if (flags & GPIO_PULL_DOWN) {
if (!rtc_gpio_is_valid_gpio(io_pin) || SOC_GPIO_SUPPORT_RTC_INDEPENDENT) {
gpio_ll_pullup_dis(&GPIO, io_pin);
gpio_ll_pulldown_en(&GPIO, io_pin);
} else {
#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
int rtcio_num = rtc_io_num_map[io_pin];
rtcio_hal_pulldown_enable(rtc_io_num_map[io_pin]);
if (rtc_io_desc[rtcio_num].pullup) {
rtcio_hal_pullup_disable(rtc_io_num_map[io_pin]);
} else {
ret = -ENOTSUP;
goto end;
}
#endif
}
}
if (flags & GPIO_OUTPUT) {
if (!gpio_pin_is_output_capable(pin)) {
LOG_ERR("GPIO can only be used as input");
ret = -EINVAL;
goto end;
}
if (flags & GPIO_SINGLE_ENDED) {
if (flags & GPIO_LINE_OPEN_DRAIN) {
gpio_ll_od_enable(cfg->gpio_base, io_pin);
} else {
LOG_ERR("GPIO configuration not supported");
ret = -ENOTSUP;
goto end;
}
} else {
gpio_ll_od_disable(cfg->gpio_base, io_pin);
}
/*
* By default, drive strength is set to its maximum value when the pin is set
* to either low or high states. Alternative drive strength is weak-only,
* while any other intermediary combination is considered invalid.
*/
switch (flags & ESP32_GPIO_DS_MASK) {
case ESP32_GPIO_DS_DFLT:
if (!rtc_gpio_is_valid_gpio(io_pin) || SOC_GPIO_SUPPORT_RTC_INDEPENDENT) {
gpio_ll_set_drive_capability(cfg->gpio_base,
io_pin,
GPIO_DRIVE_CAP_3);
} else {
#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
rtcio_hal_set_drive_capability(rtc_io_num_map[io_pin],
GPIO_DRIVE_CAP_3);
#endif
}
break;
case ESP32_GPIO_DS_ALT:
if (!rtc_gpio_is_valid_gpio(io_pin) || SOC_GPIO_SUPPORT_RTC_INDEPENDENT) {
gpio_ll_set_drive_capability(cfg->gpio_base,
io_pin,
GPIO_DRIVE_CAP_0);
} else {
#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
rtcio_hal_set_drive_capability(rtc_io_num_map[io_pin],
GPIO_DRIVE_CAP_0);
#endif
}
break;
default:
ret = -EINVAL;
goto end;
}
/* Set output pin initial value */
if (flags & GPIO_OUTPUT_INIT_HIGH) {
gpio_ll_set_level(cfg->gpio_base, io_pin, 1);
} else if (flags & GPIO_OUTPUT_INIT_LOW) {
gpio_ll_set_level(cfg->gpio_base, io_pin, 0);
}
gpio_ll_output_enable(&GPIO, io_pin);
esp_rom_gpio_matrix_out(io_pin, SIG_GPIO_OUT_IDX, false, false);
}
if (flags & GPIO_INPUT) {
gpio_ll_input_enable(&GPIO, io_pin);
}
end:
irq_unlock(key);
return ret;
}
static int gpio_esp32_port_get_raw(const struct device *port, uint32_t *value)
{
const struct gpio_esp32_config *const cfg = port->config;
if (cfg->gpio_port == 0) {
*value = cfg->gpio_dev->in;
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay)
} else {
*value = cfg->gpio_dev->in1.data;
#endif
}
return 0;
}
static int gpio_esp32_port_set_masked_raw(const struct device *port,
uint32_t mask, uint32_t value)
{
const struct gpio_esp32_config *const cfg = port->config;
uint32_t key = irq_lock();
if (cfg->gpio_port == 0) {
cfg->gpio_dev->out = (cfg->gpio_dev->out & ~mask) | (mask & value);
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay)
} else {
cfg->gpio_dev->out1.data = (cfg->gpio_dev->out1.data & ~mask) | (mask & value);
#endif
}
irq_unlock(key);
return 0;
}
static int gpio_esp32_port_set_bits_raw(const struct device *port,
uint32_t pins)
{
const struct gpio_esp32_config *const cfg = port->config;
if (cfg->gpio_port == 0) {
cfg->gpio_dev->out_w1ts = pins;
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay)
} else {
cfg->gpio_dev->out1_w1ts.data = pins;
#endif
}
return 0;
}
static int gpio_esp32_port_clear_bits_raw(const struct device *port,
uint32_t pins)
{
const struct gpio_esp32_config *const cfg = port->config;
if (cfg->gpio_port == 0) {
cfg->gpio_dev->out_w1tc = pins;
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay)
} else {
cfg->gpio_dev->out1_w1tc.data = pins;
#endif
}
return 0;
}
static int gpio_esp32_port_toggle_bits(const struct device *port,
uint32_t pins)
{
const struct gpio_esp32_config *const cfg = port->config;
uint32_t key = irq_lock();
if (cfg->gpio_port == 0) {
cfg->gpio_dev->out ^= pins;
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay)
} else {
cfg->gpio_dev->out1.data ^= pins;
#endif
}
irq_unlock(key);
return 0;
}
static int convert_int_type(enum gpio_int_mode mode,
enum gpio_int_trig trig)
{
if (mode == GPIO_INT_MODE_DISABLED) {
return GPIO_INTR_DISABLE;
}
if (mode == GPIO_INT_MODE_LEVEL) {
switch (trig) {
case GPIO_INT_TRIG_LOW:
return GPIO_INTR_LOW_LEVEL;
case GPIO_INT_TRIG_HIGH:
return GPIO_INTR_HIGH_LEVEL;
default:
return -EINVAL;
}
} else { /* edge interrupts */
switch (trig) {
case GPIO_INT_TRIG_HIGH:
return GPIO_INTR_POSEDGE;
case GPIO_INT_TRIG_LOW:
return GPIO_INTR_NEGEDGE;
case GPIO_INT_TRIG_BOTH:
return GPIO_INTR_ANYEDGE;
default:
return -EINVAL;
}
}
/* Any other type of interrupt triggering is invalid. */
return -EINVAL;
}
static int gpio_esp32_pin_interrupt_configure(const struct device *port,
gpio_pin_t pin,
enum gpio_int_mode mode,
enum gpio_int_trig trig)
{
const struct gpio_esp32_config *const cfg = port->config;
uint32_t io_pin = (uint32_t) pin + ((cfg->gpio_port == 1 && pin < 32) ? 32 : 0);
int intr_trig_mode = convert_int_type(mode, trig);
uint32_t key;
if (intr_trig_mode < 0) {
return intr_trig_mode;
}
key = irq_lock();
gpio_ll_set_intr_type(cfg->gpio_base, io_pin, intr_trig_mode);
gpio_ll_intr_enable_on_core(cfg->gpio_base, CPU_ID(), io_pin);
irq_unlock(key);
return 0;
}
static int gpio_esp32_manage_callback(const struct device *dev,
struct gpio_callback *callback,
bool set)
{
struct gpio_esp32_data *data = dev->data;
return gpio_manage_callback(&data->cb, callback, set);
}
static uint32_t gpio_esp32_get_pending_int(const struct device *dev)
{
const struct gpio_esp32_config *const cfg = dev->config;
uint32_t irq_status;
uint32_t const core_id = CPU_ID();
if (cfg->gpio_port == 0) {
gpio_ll_get_intr_status(cfg->gpio_base, core_id, &irq_status);
} else {
gpio_ll_get_intr_status_high(cfg->gpio_base, core_id, &irq_status);
}
return irq_status;
}
static void IRAM_ATTR gpio_esp32_fire_callbacks(const struct device *dev)
{
const struct gpio_esp32_config *const cfg = dev->config;
struct gpio_esp32_data *data = dev->data;
uint32_t irq_status;
uint32_t const core_id = CPU_ID();
if (cfg->gpio_port == 0) {
gpio_ll_get_intr_status(cfg->gpio_base, core_id, &irq_status);
gpio_ll_clear_intr_status(cfg->gpio_base, irq_status);
} else {
gpio_ll_get_intr_status_high(cfg->gpio_base, core_id, &irq_status);
gpio_ll_clear_intr_status_high(cfg->gpio_base, irq_status);
}
if (irq_status != 0) {
gpio_fire_callbacks(&data->cb, dev, irq_status);
}
}
static void gpio_esp32_isr(void *param);
static int gpio_esp32_init(const struct device *dev)
{
struct gpio_esp32_data *data = dev->data;
static bool isr_connected;
if (!isr_connected) {
esp_intr_alloc(DT_IRQN(DT_NODELABEL(gpio0)),
0,
(ISR_HANDLER)gpio_esp32_isr,
(void *)dev,
NULL);
isr_connected = true;
}
return 0;
}
static const struct gpio_driver_api gpio_esp32_driver_api = {
.pin_configure = gpio_esp32_config,
.port_get_raw = gpio_esp32_port_get_raw,
.port_set_masked_raw = gpio_esp32_port_set_masked_raw,
.port_set_bits_raw = gpio_esp32_port_set_bits_raw,
.port_clear_bits_raw = gpio_esp32_port_clear_bits_raw,
.port_toggle_bits = gpio_esp32_port_toggle_bits,
.pin_interrupt_configure = gpio_esp32_pin_interrupt_configure,
.manage_callback = gpio_esp32_manage_callback,
.get_pending_int = gpio_esp32_get_pending_int
};
#define ESP_SOC_GPIO_INIT(_id) \
static struct gpio_esp32_data gpio_data_##_id; \
static struct gpio_esp32_config gpio_config_##_id = { \
.drv_cfg = { \
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(_id), \
}, \
.gpio_base = (gpio_dev_t *)DT_REG_ADDR(DT_NODELABEL(gpio0)), \
.gpio_dev = (gpio_dev_t *)DT_REG_ADDR(DT_NODELABEL(gpio##_id)), \
.gpio_port = _id \
}; \
DEVICE_DT_DEFINE(DT_NODELABEL(gpio##_id), \
&gpio_esp32_init, \
NULL, \
&gpio_data_##_id, \
&gpio_config_##_id, \
PRE_KERNEL_1, \
CONFIG_GPIO_INIT_PRIORITY, \
&gpio_esp32_driver_api);
DT_INST_FOREACH_STATUS_OKAY(ESP_SOC_GPIO_INIT);
static void IRAM_ATTR gpio_esp32_isr(void *param)
{
ARG_UNUSED(param);
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio0), okay)
gpio_esp32_fire_callbacks(DEVICE_DT_INST_GET(0));
#endif
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay)
gpio_esp32_fire_callbacks(DEVICE_DT_INST_GET(1));
#endif
}