diff --git a/arch/xtensa/soc/esp32/linker.ld b/arch/xtensa/soc/esp32/linker.ld index 7d965713992..44c8eeca4b5 100644 --- a/arch/xtensa/soc/esp32/linker.ld +++ b/arch/xtensa/soc/esp32/linker.ld @@ -27,6 +27,7 @@ PROVIDE ( __stack = 0x3ffe3f20 ); PROVIDE ( uart_tx_one_char = 0x40009200 ); PROVIDE ( uart_rx_one_char = 0x400092d0 ); PROVIDE ( uartAttach = 0x40008fd0 ); +PROVIDE ( intr_matrix_set = 0x4000681c ); MEMORY { diff --git a/boards/xtensa/esp32/esp32_defconfig b/boards/xtensa/esp32/esp32_defconfig index fda6db6d0ba..e8f44823738 100644 --- a/boards/xtensa/esp32/esp32_defconfig +++ b/boards/xtensa/esp32/esp32_defconfig @@ -19,3 +19,6 @@ CONFIG_XTENSA_USE_CORE_CRT1=n CONFIG_PINMUX=y CONFIG_PINMUX_ESP32=y + +CONFIG_GPIO=y +CONFIG_GPIO_ESP32=y diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e84ae06e755..cc3048442f8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -63,4 +63,6 @@ source "drivers/gpio/Kconfig.fe310" source "drivers/gpio/Kconfig.cc2650" +source "drivers/gpio/Kconfig.esp32" + endif # GPIO diff --git a/drivers/gpio/Kconfig.esp32 b/drivers/gpio/Kconfig.esp32 new file mode 100644 index 00000000000..348b49cbb3e --- /dev/null +++ b/drivers/gpio/Kconfig.esp32 @@ -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 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ada9000fcd7..3ebcf313b58 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_GPIO_CC32XX) += gpio_cc32xx.o obj-$(CONFIG_GPIO_PULPINO) += gpio_pulpino.o obj-$(CONFIG_GPIO_FE310) += gpio_fe310.o obj-$(CONFIG_GPIO_CC2650) += gpio_cc2650.o +obj-$(CONFIG_GPIO_ESP32) += gpio_esp32.o diff --git a/drivers/gpio/gpio_esp32.c b/drivers/gpio/gpio_esp32.c new file mode 100644 index 00000000000..34dcc57f01e --- /dev/null +++ b/drivers/gpio/gpio_esp32.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +}