drivers: gpio: esp32: Add ESP32 GPIO driver
This provides basic GPIO support, with interrupts, and the ability to read and write to ports on a pin-by-pin basis. Jira: ZEP-2286 Signed-off-by: Leandro Pereira <leandro.pereira@intel.com>
This commit is contained in:
parent
f3b05c6a58
commit
c0c79a8041
6 changed files with 395 additions and 0 deletions
|
@ -27,6 +27,7 @@ PROVIDE ( __stack = 0x3ffe3f20 );
|
||||||
PROVIDE ( uart_tx_one_char = 0x40009200 );
|
PROVIDE ( uart_tx_one_char = 0x40009200 );
|
||||||
PROVIDE ( uart_rx_one_char = 0x400092d0 );
|
PROVIDE ( uart_rx_one_char = 0x400092d0 );
|
||||||
PROVIDE ( uartAttach = 0x40008fd0 );
|
PROVIDE ( uartAttach = 0x40008fd0 );
|
||||||
|
PROVIDE ( intr_matrix_set = 0x4000681c );
|
||||||
|
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,3 +19,6 @@ CONFIG_XTENSA_USE_CORE_CRT1=n
|
||||||
|
|
||||||
CONFIG_PINMUX=y
|
CONFIG_PINMUX=y
|
||||||
CONFIG_PINMUX_ESP32=y
|
CONFIG_PINMUX_ESP32=y
|
||||||
|
|
||||||
|
CONFIG_GPIO=y
|
||||||
|
CONFIG_GPIO_ESP32=y
|
||||||
|
|
|
@ -63,4 +63,6 @@ source "drivers/gpio/Kconfig.fe310"
|
||||||
|
|
||||||
source "drivers/gpio/Kconfig.cc2650"
|
source "drivers/gpio/Kconfig.cc2650"
|
||||||
|
|
||||||
|
source "drivers/gpio/Kconfig.esp32"
|
||||||
|
|
||||||
endif # GPIO
|
endif # GPIO
|
||||||
|
|
44
drivers/gpio/Kconfig.esp32
Normal file
44
drivers/gpio/Kconfig.esp32
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# Kconfig.esp32 - ESP32 GPIO configuration options
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017 Intel Corporation
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
menuconfig GPIO_ESP32
|
||||||
|
bool "ESP32 GPIO"
|
||||||
|
depends on GPIO && SOC_ESP32
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enables the ESP32 GPIO driver
|
||||||
|
|
||||||
|
if GPIO_ESP32
|
||||||
|
|
||||||
|
config GPIO_ESP32_IRQ
|
||||||
|
int "IRQ line for ESP32 GPIO pins"
|
||||||
|
default 10
|
||||||
|
|
||||||
|
config GPIO_ESP32_0
|
||||||
|
bool "ESP32 GPIO (pins 0-31)"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Include support for GPIO pins 0-31 on the ESP32.
|
||||||
|
|
||||||
|
config GPIO_ESP32_0_NAME
|
||||||
|
string "Driver name"
|
||||||
|
depends on GPIO_ESP32_0
|
||||||
|
default "GPIO_0"
|
||||||
|
|
||||||
|
config GPIO_ESP32_1
|
||||||
|
bool "ESP32 GPIO (pins 32-39)"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Include support for GPIO pins 32-39 on the ESP32.
|
||||||
|
|
||||||
|
config GPIO_ESP32_1_NAME
|
||||||
|
string "Driver name"
|
||||||
|
depends on GPIO_ESP32_1
|
||||||
|
default "GPIO_1"
|
||||||
|
|
||||||
|
endif # GPIO_ESP32
|
|
@ -15,3 +15,4 @@ obj-$(CONFIG_GPIO_CC32XX) += gpio_cc32xx.o
|
||||||
obj-$(CONFIG_GPIO_PULPINO) += gpio_pulpino.o
|
obj-$(CONFIG_GPIO_PULPINO) += gpio_pulpino.o
|
||||||
obj-$(CONFIG_GPIO_FE310) += gpio_fe310.o
|
obj-$(CONFIG_GPIO_FE310) += gpio_fe310.o
|
||||||
obj-$(CONFIG_GPIO_CC2650) += gpio_cc2650.o
|
obj-$(CONFIG_GPIO_CC2650) += gpio_cc2650.o
|
||||||
|
obj-$(CONFIG_GPIO_ESP32) += gpio_esp32.o
|
||||||
|
|
344
drivers/gpio/gpio_esp32.c
Normal file
344
drivers/gpio/gpio_esp32.c
Normal file
|
@ -0,0 +1,344 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <rom/ets_sys.h>
|
||||||
|
#include <soc/dport_reg.h>
|
||||||
|
#include <soc/gpio_reg.h>
|
||||||
|
#include <soc/soc.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <device.h>
|
||||||
|
#include <gpio.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <misc/util.h>
|
||||||
|
#include <pinmux.h>
|
||||||
|
|
||||||
|
#include "gpio_utils.h"
|
||||||
|
|
||||||
|
struct gpio_esp32_data {
|
||||||
|
struct device *pinmux;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
volatile u32_t *set_reg;
|
||||||
|
volatile u32_t *clear_reg;
|
||||||
|
} write;
|
||||||
|
struct {
|
||||||
|
volatile u32_t *reg;
|
||||||
|
} read;
|
||||||
|
struct {
|
||||||
|
volatile u32_t *status_reg;
|
||||||
|
volatile u32_t *ack_reg;
|
||||||
|
} irq;
|
||||||
|
int pin_offset;
|
||||||
|
} port;
|
||||||
|
|
||||||
|
u32_t cb_pins;
|
||||||
|
sys_slist_t cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int convert_int_type(int flags)
|
||||||
|
{
|
||||||
|
/* Reference: "ESP32 Technical Reference Manual", "IO_MUX and
|
||||||
|
* GPIO matrix"; "GPIO_PINn_INT_TYPE".
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!(flags & GPIO_INT)) {
|
||||||
|
return 0; /* Disables interrupt for a pin. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & GPIO_INT_EDGE) == GPIO_INT_EDGE) {
|
||||||
|
if ((flags & GPIO_INT_ACTIVE_LOW) == GPIO_INT_ACTIVE_LOW) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & GPIO_INT_DOUBLE_EDGE) == GPIO_INT_DOUBLE_EDGE) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1; /* Defaults to rising edge. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & GPIO_INT_LEVEL) == GPIO_INT_LEVEL) {
|
||||||
|
if ((flags & GPIO_INT_ACTIVE_LOW) == GPIO_INT_ACTIVE_LOW) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 5; /* Defaults to high level. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Any other type of interrupt triggering is invalid. */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32_t *gpio_pin_reg(int pin)
|
||||||
|
{
|
||||||
|
return (u32_t *)(GPIO_PIN0_REG + pin * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_interrupt(u32_t pin, int flags)
|
||||||
|
{
|
||||||
|
volatile u32_t *reg = gpio_pin_reg(pin);
|
||||||
|
int type = convert_int_type(flags);
|
||||||
|
u32_t v;
|
||||||
|
int key;
|
||||||
|
|
||||||
|
if (type < 0) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = irq_lock();
|
||||||
|
|
||||||
|
v = *reg;
|
||||||
|
v &= ~(GPIO_PIN_INT_ENA_M | GPIO_PIN_INT_TYPE_M);
|
||||||
|
/* Bit 3 of INT_ENA will enable interrupts on CPU 0 */
|
||||||
|
v |= (1<<2) << GPIO_PIN_INT_ENA_S;
|
||||||
|
/* Interrupt triggering mode */
|
||||||
|
v |= type << GPIO_PIN_INT_TYPE_S;
|
||||||
|
*reg = v;
|
||||||
|
|
||||||
|
irq_unlock(key);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_polarity(u32_t pin, int flags)
|
||||||
|
{
|
||||||
|
volatile u32_t *reg = (u32_t *)(GPIO_FUNC0_IN_SEL_CFG_REG + 4 * pin);
|
||||||
|
|
||||||
|
if (flags & GPIO_POL_INV) {
|
||||||
|
*reg |= BIT(GPIO_FUNC0_IN_INV_SEL_S);
|
||||||
|
} else {
|
||||||
|
*reg &= ~BIT(GPIO_FUNC0_IN_INV_SEL_S);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_esp32_config(struct device *dev, int access_op,
|
||||||
|
u32_t pin, int flags)
|
||||||
|
{
|
||||||
|
struct gpio_esp32_data *data = dev->driver_data;
|
||||||
|
u32_t func;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (access_op != GPIO_ACCESS_BY_PIN) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Query pinmux to validate pin number. */
|
||||||
|
r = pinmux_pin_get(data->pinmux, pin, &func);
|
||||||
|
if (r < 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
pinmux_pin_set(data->pinmux, pin, PINMUX_FUNC_A);
|
||||||
|
if (flags & GPIO_PUD_PULL_UP) {
|
||||||
|
pinmux_pin_pullup(data->pinmux, pin, PINMUX_PULLUP_ENABLE);
|
||||||
|
} else if (flags & GPIO_PUD_PULL_DOWN) {
|
||||||
|
pinmux_pin_pullup(data->pinmux, pin, PINMUX_PULLUP_DISABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & GPIO_DIR_OUT) {
|
||||||
|
pinmux_pin_input_enable(data->pinmux, pin,
|
||||||
|
PINMUX_OUTPUT_ENABLED);
|
||||||
|
} else {
|
||||||
|
pinmux_pin_input_enable(data->pinmux, pin,
|
||||||
|
PINMUX_INPUT_ENABLED);
|
||||||
|
config_polarity(pin, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config_interrupt(pin, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_esp32_write(struct device *dev, int access_op,
|
||||||
|
u32_t pin, u32_t value)
|
||||||
|
{
|
||||||
|
struct gpio_esp32_data *data = dev->driver_data;
|
||||||
|
u32_t v;
|
||||||
|
|
||||||
|
if (access_op != GPIO_ACCESS_BY_PIN) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = BIT(pin - data->port.pin_offset);
|
||||||
|
if (value) {
|
||||||
|
*data->port.write.set_reg = v;
|
||||||
|
} else {
|
||||||
|
*data->port.write.clear_reg = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_esp32_read(struct device *dev, int access_op,
|
||||||
|
u32_t pin, u32_t *value)
|
||||||
|
{
|
||||||
|
struct gpio_esp32_data *data = dev->driver_data;
|
||||||
|
u32_t v;
|
||||||
|
|
||||||
|
if (access_op != GPIO_ACCESS_BY_PIN) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = *data->port.read.reg;
|
||||||
|
*value = !!(v & BIT(pin - data->port.pin_offset));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_esp32_manage_callback(struct device *dev,
|
||||||
|
struct gpio_callback *callback,
|
||||||
|
bool set)
|
||||||
|
{
|
||||||
|
struct gpio_esp32_data *data = dev->driver_data;
|
||||||
|
|
||||||
|
_gpio_manage_callback(&data->cb, callback, set);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_esp32_enable_callback(struct device *dev,
|
||||||
|
int access_op, u32_t pin)
|
||||||
|
{
|
||||||
|
struct gpio_esp32_data *data = dev->driver_data;
|
||||||
|
|
||||||
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||||
|
data->cb_pins |= BIT(pin);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_esp32_disable_callback(struct device *dev,
|
||||||
|
int access_op, u32_t pin)
|
||||||
|
{
|
||||||
|
struct gpio_esp32_data *data = dev->driver_data;
|
||||||
|
|
||||||
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||||
|
data->cb_pins &= ~BIT(pin);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpio_esp32_fire_callbacks(struct device *device)
|
||||||
|
{
|
||||||
|
struct gpio_esp32_data *data = device->driver_data;
|
||||||
|
u32_t values = *data->port.irq.status_reg;
|
||||||
|
|
||||||
|
if (values & data->cb_pins) {
|
||||||
|
_gpio_fire_callbacks(&data->cb, device, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
*data->port.irq.ack_reg = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpio_esp32_isr(void *param);
|
||||||
|
|
||||||
|
static int gpio_esp32_init(struct device *device)
|
||||||
|
{
|
||||||
|
struct gpio_esp32_data *data = device->driver_data;
|
||||||
|
static bool isr_connected;
|
||||||
|
|
||||||
|
data->pinmux = device_get_binding(CONFIG_PINMUX_NAME);
|
||||||
|
if (!data->pinmux) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isr_connected) {
|
||||||
|
irq_disable(CONFIG_GPIO_ESP32_IRQ);
|
||||||
|
|
||||||
|
IRQ_CONNECT(CONFIG_GPIO_ESP32_IRQ, 1, gpio_esp32_isr,
|
||||||
|
NULL, 0);
|
||||||
|
|
||||||
|
intr_matrix_set(0, ETS_GPIO_INTR_SOURCE,
|
||||||
|
CONFIG_GPIO_ESP32_IRQ);
|
||||||
|
|
||||||
|
irq_enable(CONFIG_GPIO_ESP32_IRQ);
|
||||||
|
|
||||||
|
isr_connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct gpio_driver_api gpio_esp32_driver = {
|
||||||
|
.config = gpio_esp32_config,
|
||||||
|
.write = gpio_esp32_write,
|
||||||
|
.read = gpio_esp32_read,
|
||||||
|
.manage_callback = gpio_esp32_manage_callback,
|
||||||
|
.enable_callback = gpio_esp32_enable_callback,
|
||||||
|
.disable_callback = gpio_esp32_disable_callback,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct gpio_esp32_data gpio_data_pins_0_to_31 = {
|
||||||
|
.port = {
|
||||||
|
.write = {
|
||||||
|
.set_reg = (u32_t *)GPIO_OUT_W1TS_REG,
|
||||||
|
.clear_reg = (u32_t *)GPIO_OUT_W1TC_REG,
|
||||||
|
},
|
||||||
|
.read = {
|
||||||
|
.reg = (u32_t *)GPIO_IN_REG,
|
||||||
|
},
|
||||||
|
.irq = {
|
||||||
|
.status_reg = (u32_t *)GPIO_STATUS_REG,
|
||||||
|
.ack_reg = (u32_t *)GPIO_STATUS_W1TC_REG,
|
||||||
|
},
|
||||||
|
.pin_offset = 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static struct gpio_esp32_data gpio_data_pins_32_to_63 = {
|
||||||
|
.port = {
|
||||||
|
.write = {
|
||||||
|
.set_reg = (u32_t *)GPIO_OUT1_W1TS_REG,
|
||||||
|
.clear_reg = (u32_t *)GPIO_OUT1_W1TC_REG,
|
||||||
|
},
|
||||||
|
.read = {
|
||||||
|
.reg = (u32_t *)GPIO_IN1_REG,
|
||||||
|
},
|
||||||
|
.irq = {
|
||||||
|
.status_reg = (u32_t *)GPIO_STATUS1_REG,
|
||||||
|
.ack_reg = (u32_t *)GPIO_STATUS1_W1TC_REG,
|
||||||
|
},
|
||||||
|
.pin_offset = 32,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GPIO_DEVICE_INIT(__name, __data_struct_name) \
|
||||||
|
DEVICE_AND_API_INIT(gpio_esp32_ ## __data_struct_name, \
|
||||||
|
__name, \
|
||||||
|
gpio_esp32_init, \
|
||||||
|
&gpio_data_pins_ ## __data_struct_name, \
|
||||||
|
NULL, \
|
||||||
|
POST_KERNEL, \
|
||||||
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
||||||
|
&gpio_esp32_driver)
|
||||||
|
|
||||||
|
/* GPIOs are divided in two groups for ESP32 because the callback
|
||||||
|
* API works with 32-bit bitmasks to manage interrupt callbacks,
|
||||||
|
* and the device has 40 GPIO pins.
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_GPIO_ESP32_0)
|
||||||
|
GPIO_DEVICE_INIT(CONFIG_GPIO_ESP32_0_NAME, 0_to_31);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_GPIO_ESP32_1)
|
||||||
|
GPIO_DEVICE_INIT(CONFIG_GPIO_ESP32_1_NAME, 32_to_63);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void gpio_esp32_isr(void *param)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_GPIO_ESP32_0)
|
||||||
|
gpio_esp32_fire_callbacks(DEVICE_GET(gpio_esp32_0_to_31));
|
||||||
|
#endif
|
||||||
|
#if defined(CONFIG_GPIO_ESP32_1)
|
||||||
|
gpio_esp32_fire_callbacks(DEVICE_GET(gpio_esp32_32_to_63));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ARG_UNUSED(param);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue