drivers: gpio_esp32: update to use new GPIO API

- Updates gpio driver and device tree files to the new GPIO Config flags
- Implements the new port_* APIs
- Update I2C and PWM Drivers to use new GPIO config
- Add esp32.overlay to gpio_basic_api test
- refactor convert_int_type, regs struct
- remove config_polarity
- add kConfig notes

Tests:
- samples/basic/blinky
- samples/basic/button
- tests/drivers/gpio/gpio_basic_api
- tests/drivers/gpio/gpio_api_1pin

Board:
- esp32 DevKitC V4

Note about interrupts:
The ESP32 requires specifying a CPU interrupt to be used for GPIO
interrupt signals.  CPU interrupts can be either level or edge (or
special) triggered, but not both.
Please check gpio/Kconfig.esp32 for more info.

Signed-off-by: Mohamed ElShahawi <ExtremeGTX@hotmail.com>
This commit is contained in:
Mohamed ElShahawi 2019-10-24 15:30:08 +02:00 committed by Carles Cufí
commit fef3ebaa69
6 changed files with 269 additions and 164 deletions

View file

@ -20,7 +20,7 @@
leds { leds {
compatible = "gpio-leds"; compatible = "gpio-leds";
blue_led: led { blue_led: led {
gpios = <&gpio0 2 GPIO_INT_ACTIVE_HIGH>; gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
label = "Status Led"; label = "Status Led";
}; };
}; };
@ -29,23 +29,23 @@
compatible = "gpio-keys"; compatible = "gpio-keys";
menu_button: menu_button { menu_button: menu_button {
label = "Menu"; label = "Menu";
gpios = <&gpio0 13 GPIO_INT_ACTIVE_LOW>; gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
}; };
select_button: select_button { select_button: select_button {
label = "Select"; label = "Select";
gpios = <&gpio0 27 GPIO_INT_ACTIVE_LOW>; gpios = <&gpio0 27 GPIO_ACTIVE_LOW>;
}; };
a_button: a_button { a_button: a_button {
label = "A"; label = "A";
gpios = <&gpio1 0 GPIO_INT_ACTIVE_LOW>; gpios = <&gpio1 0 GPIO_ACTIVE_LOW>;
}; };
b_button: b_button { b_button: b_button {
label = "B"; label = "B";
gpios = <&gpio1 1 GPIO_INT_ACTIVE_LOW>; gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
}; };
start_button: start_button { start_button: start_button {
label = "Start"; label = "Start";
gpios = <&gpio1 7 GPIO_INT_ACTIVE_LOW>; gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
}; };
}; };

View file

@ -15,6 +15,14 @@ if GPIO_ESP32
config GPIO_ESP32_IRQ config GPIO_ESP32_IRQ
int "IRQ line for ESP32 GPIO pins" int "IRQ line for ESP32 GPIO pins"
default 10 default 10
help
Select the IRQ line to be used for GPIO interrupts.
Edge-triggered interrupts are supported on lines: 10, 22,
28, 30.
Level-triggered interrupts are supported on lines: 0-5, 8,
9, 12, 13, 17-21, 23-27, 31.
config GPIO_ESP32_0 config GPIO_ESP32_0
bool "ESP32 GPIO (pins 0-31)" bool "ESP32 GPIO (pins 0-31)"

View file

@ -20,23 +20,34 @@
#include "gpio_utils.h" #include "gpio_utils.h"
#define GET_GPIO_PIN_REG(pin) ((u32_t *)GPIO_REG(pin))
/* ESP3 TRM v4.0 and gpio_reg.h header both incorrectly identify bit3
* as being the bit selecting PRO CPU interrupt enable. It's actually
* bit2. bit4 and bit5 are also shifted.
*/
#define GPIO_CPU0_INT_ENABLE (BIT(2) << GPIO_PIN_INT_ENA_S)
/* ESP3 TRM table 8: CPU Interrupts
*
* Edge-triggered are: 10, 22, 28, 30
* Level-triggered are: 0-5, 8, 9, 12, 13, 17-21, 23-27, 31
*/
#define ESP32_IRQ_EDGE_TRIG 0x50400400
#define ESP32_IRQ_LEVEL_TRIG 0x8fbe333f
struct gpio_esp32_data { struct gpio_esp32_data {
/* gpio_driver_data needs to be first */ /* gpio_driver_data needs to be first */
struct gpio_driver_data common; struct gpio_driver_data common;
struct device *pinmux; struct device *pinmux;
struct { struct {
struct { volatile u32_t *set_reg;
volatile u32_t *set_reg; volatile u32_t *clear_reg;
volatile u32_t *clear_reg; volatile u32_t *input_reg;
} write; volatile u32_t *output_reg;
struct { volatile u32_t *irq_status_reg;
volatile u32_t *reg; volatile u32_t *irq_ack_reg;
} read;
struct {
volatile u32_t *status_reg;
volatile u32_t *ack_reg;
} irq;
int pin_offset; int pin_offset;
} port; } port;
@ -44,97 +55,12 @@ struct gpio_esp32_data {
sys_slist_t cb; 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_HIGH) == GPIO_INT_ACTIVE_HIGH) {
return 1;
}
if ((flags & GPIO_INT_DOUBLE_EDGE) == GPIO_INT_DOUBLE_EDGE) {
return 3;
}
return 2; /* Defaults to falling edge. */
}
if ((flags & GPIO_INT_EDGE) == GPIO_INT_LEVEL) {
if ((flags & GPIO_INT_ACTIVE_HIGH) == GPIO_INT_ACTIVE_HIGH) {
return 5;
}
return 4; /* Defaults to low 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;
unsigned 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 + pin * 4U);
if (flags & GPIO_POL_INV) {
*reg |= BIT(GPIO_FUNC0_IN_INV_SEL_S);
} else {
*reg &= ~BIT(GPIO_FUNC0_IN_INV_SEL_S);
}
}
static void config_drive_strength(u32_t pin, int flags)
{
volatile u32_t *reg = gpio_pin_reg(pin);
if ((flags & GPIO_DS_DISCONNECT_LOW) == GPIO_DS_DISCONNECT_LOW) {
*reg |= GPIO_PIN_PAD_DRIVER;
} else {
*reg &= ~GPIO_PIN_PAD_DRIVER;
}
}
static int gpio_esp32_config(struct device *dev, int access_op, static int gpio_esp32_config(struct device *dev, int access_op,
u32_t pin, int flags) u32_t pin, int flags)
{ {
struct gpio_esp32_data *data = dev->driver_data; struct gpio_esp32_data *data = dev->driver_data;
u32_t io_pin = pin + data->port.pin_offset; /* Range from 0 - 39 */
u32_t *reg = GET_GPIO_PIN_REG(io_pin);
u32_t func; u32_t func;
int r; int r;
@ -143,31 +69,52 @@ static int gpio_esp32_config(struct device *dev, int access_op,
} }
/* Query pinmux to validate pin number. */ /* Query pinmux to validate pin number. */
r = pinmux_pin_get(data->pinmux, pin, &func); r = pinmux_pin_get(data->pinmux, io_pin, &func);
if (r < 0) { if (r < 0) {
return r; return r;
} }
pinmux_pin_set(data->pinmux, pin, PIN_FUNC_GPIO); /* Set pin function as GPIO */
if (flags & GPIO_PUD_PULL_UP) { pinmux_pin_set(data->pinmux, io_pin, PIN_FUNC_GPIO);
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_PULL_UP) {
pinmux_pin_pullup(data->pinmux, io_pin, PINMUX_PULLUP_ENABLE);
} else if (flags & GPIO_PULL_DOWN) {
pinmux_pin_pullup(data->pinmux, io_pin, PINMUX_PULLUP_DISABLE);
} }
if (flags & GPIO_DIR_OUT) { if (flags & GPIO_OUTPUT) {
r = pinmux_pin_input_enable(data->pinmux, pin,
PINMUX_OUTPUT_ENABLED); if (flags & GPIO_SINGLE_ENDED) {
assert(r >= 0); if (flags & GPIO_LINE_OPEN_DRAIN) {
} else { *reg |= GPIO_PIN_PAD_DRIVER;
pinmux_pin_input_enable(data->pinmux, pin, } else {
r = -ENOTSUP;
}
} else {
*reg &= ~GPIO_PIN_PAD_DRIVER;
}
/* Set output pin initial value */
if (flags & GPIO_OUTPUT_INIT_HIGH) {
*data->port.set_reg = BIT(pin);
} else if (flags & GPIO_OUTPUT_INIT_LOW) {
*data->port.clear_reg = BIT(pin);
}
r = pinmux_pin_input_enable(data->pinmux, io_pin,
PINMUX_OUTPUT_ENABLED);
if (r < 0) {
return r;
}
} else { /* Input */
pinmux_pin_input_enable(data->pinmux, io_pin,
PINMUX_INPUT_ENABLED); PINMUX_INPUT_ENABLED);
config_polarity(pin, flags);
} }
config_drive_strength(pin, flags); return 0;
return config_interrupt(pin, flags);
} }
static int gpio_esp32_write(struct device *dev, int access_op, static int gpio_esp32_write(struct device *dev, int access_op,
@ -182,9 +129,9 @@ static int gpio_esp32_write(struct device *dev, int access_op,
v = BIT(pin - data->port.pin_offset); v = BIT(pin - data->port.pin_offset);
if (value) { if (value) {
*data->port.write.set_reg = v; *data->port.set_reg = v;
} else { } else {
*data->port.write.clear_reg = v; *data->port.clear_reg = v;
} }
return 0; return 0;
@ -200,12 +147,147 @@ static int gpio_esp32_read(struct device *dev, int access_op,
return -ENOTSUP; return -ENOTSUP;
} }
v = *data->port.read.reg; v = *data->port.input_reg;
*value = !!(v & BIT(pin - data->port.pin_offset)); *value = !!(v & BIT(pin - data->port.pin_offset));
return 0; return 0;
} }
static int gpio_esp32_port_get_raw(struct device *port, u32_t *value)
{
struct gpio_esp32_data *data = port->driver_data;
*value = *data->port.input_reg;
return 0;
}
static int gpio_esp32_port_set_masked_raw(struct device *port,
u32_t mask, u32_t value)
{
struct gpio_esp32_data *data = port->driver_data;
u32_t key;
key = irq_lock();
*data->port.output_reg = (*data->port.output_reg & ~mask)
| (mask & value);
irq_unlock(key);
return 0;
}
static int gpio_esp32_port_set_bits_raw(struct device *port,
u32_t pins)
{
struct gpio_esp32_data *data = port->driver_data;
*data->port.set_reg = pins;
return 0;
}
static int gpio_esp32_port_clear_bits_raw(struct device *port,
u32_t pins)
{
struct gpio_esp32_data *data = port->driver_data;
*data->port.clear_reg = pins;
return 0;
}
static int gpio_esp32_port_toggle_bits(struct device *port,
u32_t pins)
{
struct gpio_esp32_data *data = port->driver_data;
u32_t key;
key = irq_lock();
*data->port.output_reg = (*data->port.output_reg ^ pins);
irq_unlock(key);
return 0;
}
static int convert_int_type(enum gpio_int_mode mode,
enum gpio_int_trig trig)
{
/* Reference: "ESP32 Technical Reference Manual" > "IO_MUX and
* GPIO matrix" > "GPIO_PINn_INT_TYPE".
*/
if (mode == GPIO_INT_MODE_DISABLED) {
return 0; /* Disables interrupt for a pin. */
}
if (mode == GPIO_INT_MODE_LEVEL) {
if ((ESP32_IRQ_LEVEL_TRIG & BIT(CONFIG_GPIO_ESP32_IRQ)) == 0) {
return -ENOTSUP;
}
switch (trig) {
case GPIO_INT_TRIG_LOW:
return 4;
case GPIO_INT_TRIG_HIGH:
return 5;
default:
return -EINVAL;
}
} else { /* edge interrupts */
if ((ESP32_IRQ_EDGE_TRIG & BIT(CONFIG_GPIO_ESP32_IRQ)) == 0) {
return -ENOTSUP;
}
switch (trig) {
case GPIO_INT_TRIG_HIGH:
return 1;
case GPIO_INT_TRIG_LOW:
return 2;
case GPIO_INT_TRIG_BOTH:
/* This is supposed to work but doesn't */
return -ENOTSUP; /* 3 == any edge */
default:
return -EINVAL;
}
}
/* Any other type of interrupt triggering is invalid. */
return -EINVAL;
}
static int gpio_esp32_pin_interrupt_configure(struct device *port,
unsigned int pin,
enum gpio_int_mode mode,
enum gpio_int_trig trig)
{
struct gpio_esp32_data *data = port->driver_data;
u32_t io_pin = pin + data->port.pin_offset; /* Range from 0 - 39 */
u32_t *reg = GET_GPIO_PIN_REG(io_pin);
int intr_trig_mode = convert_int_type(mode, trig);
u32_t reg_val;
u32_t key;
if (intr_trig_mode < 0) {
return intr_trig_mode;
}
if (mode == GPIO_INT_MODE_DISABLED) {
data->cb_pins &= ~BIT(pin);
} else {
data->cb_pins |= BIT(pin);
}
key = irq_lock();
reg_val = *reg;
reg_val &= ~(GPIO_PIN_INT_ENA_M | GPIO_PIN_INT_TYPE_M);
/* Enable Interrupt on CPU0 (PRO_CPU) */
reg_val |= GPIO_CPU0_INT_ENABLE;
/* Interrupt triggering mode */
reg_val |= intr_trig_mode << GPIO_PIN_INT_TYPE_S;
*reg = reg_val;
irq_unlock(key);
return 0;
}
static int gpio_esp32_manage_callback(struct device *dev, static int gpio_esp32_manage_callback(struct device *dev,
struct gpio_callback *callback, struct gpio_callback *callback,
bool set) bool set)
@ -246,13 +328,12 @@ static int gpio_esp32_disable_callback(struct device *dev,
static void gpio_esp32_fire_callbacks(struct device *device) static void gpio_esp32_fire_callbacks(struct device *device)
{ {
struct gpio_esp32_data *data = device->driver_data; struct gpio_esp32_data *data = device->driver_data;
u32_t values = *data->port.irq.status_reg; u32_t values = *data->port.irq_status_reg;
*data->port.irq_ack_reg = values;
if (values & data->cb_pins) { if (values & data->cb_pins) {
gpio_fire_callbacks(&data->cb, device, values); gpio_fire_callbacks(&data->cb, device, values);
} }
*data->port.irq.ack_reg = values;
} }
static void gpio_esp32_isr(void *param); static void gpio_esp32_isr(void *param);
@ -288,6 +369,12 @@ static const struct gpio_driver_api gpio_esp32_driver = {
.config = gpio_esp32_config, .config = gpio_esp32_config,
.write = gpio_esp32_write, .write = gpio_esp32_write,
.read = gpio_esp32_read, .read = gpio_esp32_read,
.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, .manage_callback = gpio_esp32_manage_callback,
.enable_callback = gpio_esp32_enable_callback, .enable_callback = gpio_esp32_enable_callback,
.disable_callback = gpio_esp32_disable_callback, .disable_callback = gpio_esp32_disable_callback,
@ -296,17 +383,12 @@ static const struct gpio_driver_api gpio_esp32_driver = {
#if defined(CONFIG_GPIO_ESP32_0) #if defined(CONFIG_GPIO_ESP32_0)
static struct gpio_esp32_data gpio_data_pins_0_to_31 = { static struct gpio_esp32_data gpio_data_pins_0_to_31 = {
.port = { .port = {
.write = { .set_reg = (u32_t *)GPIO_OUT_W1TS_REG,
.set_reg = (u32_t *)GPIO_OUT_W1TS_REG, .clear_reg = (u32_t *)GPIO_OUT_W1TC_REG,
.clear_reg = (u32_t *)GPIO_OUT_W1TC_REG, .input_reg = (u32_t *)GPIO_IN_REG,
}, .output_reg = (u32_t *)GPIO_OUT_REG,
.read = { .irq_status_reg = (u32_t *)GPIO_STATUS_REG,
.reg = (u32_t *)GPIO_IN_REG, .irq_ack_reg = (u32_t *)GPIO_STATUS_W1TC_REG,
},
.irq = {
.status_reg = (u32_t *)GPIO_STATUS_REG,
.ack_reg = (u32_t *)GPIO_STATUS_W1TC_REG,
},
.pin_offset = 0, .pin_offset = 0,
} }
}; };
@ -315,30 +397,25 @@ static struct gpio_esp32_data gpio_data_pins_0_to_31 = {
#if defined(CONFIG_GPIO_ESP32_1) #if defined(CONFIG_GPIO_ESP32_1)
static struct gpio_esp32_data gpio_data_pins_32_to_39 = { static struct gpio_esp32_data gpio_data_pins_32_to_39 = {
.port = { .port = {
.write = { .set_reg = (u32_t *)GPIO_OUT1_W1TS_REG,
.set_reg = (u32_t *)GPIO_OUT1_W1TS_REG, .clear_reg = (u32_t *)GPIO_OUT1_W1TC_REG,
.clear_reg = (u32_t *)GPIO_OUT1_W1TC_REG, .input_reg = (u32_t *)GPIO_IN1_REG,
}, .output_reg = (u32_t *)GPIO_OUT1_REG,
.read = { .irq_status_reg = (u32_t *)GPIO_STATUS1_REG,
.reg = (u32_t *)GPIO_IN1_REG, .irq_ack_reg = (u32_t *)GPIO_STATUS1_W1TC_REG,
},
.irq = {
.status_reg = (u32_t *)GPIO_STATUS1_REG,
.ack_reg = (u32_t *)GPIO_STATUS1_W1TC_REG,
},
.pin_offset = 32, .pin_offset = 32,
} }
}; };
#endif #endif
#define GPIO_DEVICE_INIT(__name, __data_struct_name) \ #define GPIO_DEVICE_INIT(__name, __data_struct_name) \
DEVICE_AND_API_INIT(gpio_esp32_ ## __data_struct_name, \ DEVICE_AND_API_INIT(gpio_esp32_ ## __data_struct_name, \
__name, \ __name, \
gpio_esp32_init, \ gpio_esp32_init, \
&gpio_data_pins_ ## __data_struct_name, \ &gpio_data_pins_ ## __data_struct_name, \
NULL, \ NULL, \
POST_KERNEL, \ POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&gpio_esp32_driver) &gpio_esp32_driver)
/* GPIOs are divided in two groups for ESP32 because the callback /* GPIOs are divided in two groups for ESP32 because the callback
@ -355,6 +432,7 @@ GPIO_DEVICE_INIT(DT_INST_1_ESPRESSIF_ESP32_GPIO_LABEL, 32_to_39);
static void gpio_esp32_isr(void *param) static void gpio_esp32_isr(void *param)
{ {
#if defined(CONFIG_GPIO_ESP32_0) #if defined(CONFIG_GPIO_ESP32_0)
gpio_esp32_fire_callbacks(DEVICE_GET(gpio_esp32_0_to_31)); gpio_esp32_fire_callbacks(DEVICE_GET(gpio_esp32_0_to_31));
#endif #endif

View file

@ -98,9 +98,9 @@ struct i2c_esp32_config {
static int i2c_esp32_configure_pins(int pin, int matrix_out, int matrix_in) static int i2c_esp32_configure_pins(int pin, int matrix_out, int matrix_in)
{ {
const int pin_mode = GPIO_DIR_OUT | const int pin_mode = GPIO_OUTPUT_HIGH |
GPIO_DS_DISCONNECT_LOW | GPIO_OPEN_DRAIN |
GPIO_PUD_PULL_UP; GPIO_PULL_UP;
const char *device_name = gpio_esp32_get_gpio_for_pin(pin); const char *device_name = gpio_esp32_get_gpio_for_pin(pin);
struct device *gpio; struct device *gpio;
int ret; int ret;
@ -118,11 +118,6 @@ static int i2c_esp32_configure_pins(int pin, int matrix_out, int matrix_in)
return ret; return ret;
} }
ret = gpio_pin_write(gpio, pin, 1);
if (ret < 0) {
return ret;
}
esp32_rom_gpio_matrix_out(pin, matrix_out, false, false); esp32_rom_gpio_matrix_out(pin, matrix_out, false, false);
esp32_rom_gpio_matrix_in(pin, matrix_in, false); esp32_rom_gpio_matrix_in(pin, matrix_in, false);

View file

@ -208,7 +208,7 @@ static void pwm_led_esp32_bind_channel_timer(int speed_mode,
static int pwm_led_esp32_channel_set(int pin, bool speed_mode, int channel, static int pwm_led_esp32_channel_set(int pin, bool speed_mode, int channel,
int duty, int timer) int duty, int timer)
{ {
const int pin_mode = GPIO_DIR_OUT; const int pin_mode = GPIO_OUTPUT;
const char *device_name; const char *device_name;
struct device *gpio; struct device *gpio;

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2019 Mohamed ElShahawi
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
resources {
compatible = "test,gpio_basic_api";
out-gpios = <&gpio0 16 0>;
in-gpios = <&gpio0 17 0>;
};
};
/*
* Some notes about esp32 pins:
* GPIO pins 34-39 are not suitable for this test because:
* 1. input-only
* 2. No internal pull-up/pull-down circuitry.
* The pin names are: SENSOR_VP(GPIO36),SENSOR_CAPP(GPIO37),
* SENSOR_CAPN (GPIO38), SENSOR_VN (GPIO39),
* VDET_1 (GPIO34), VDET_2 (GPIO35).
*/