From fef3ebaa695f4ff503efc1274ad6b0ba40529fcb Mon Sep 17 00:00:00 2001 From: Mohamed ElShahawi Date: Thu, 24 Oct 2019 15:30:08 +0200 Subject: [PATCH] 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 --- boards/xtensa/odroid_go/odroid_go.dts | 12 +- drivers/gpio/Kconfig.esp32 | 8 + drivers/gpio/gpio_esp32.c | 376 +++++++++++------- drivers/i2c/i2c_esp32.c | 11 +- drivers/pwm/pwm_led_esp32.c | 2 +- .../gpio/gpio_basic_api/boards/esp32.overlay | 24 ++ 6 files changed, 269 insertions(+), 164 deletions(-) create mode 100644 tests/drivers/gpio/gpio_basic_api/boards/esp32.overlay diff --git a/boards/xtensa/odroid_go/odroid_go.dts b/boards/xtensa/odroid_go/odroid_go.dts index 2989878b199..5d73b925ce6 100644 --- a/boards/xtensa/odroid_go/odroid_go.dts +++ b/boards/xtensa/odroid_go/odroid_go.dts @@ -20,7 +20,7 @@ leds { compatible = "gpio-leds"; blue_led: led { - gpios = <&gpio0 2 GPIO_INT_ACTIVE_HIGH>; + gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>; label = "Status Led"; }; }; @@ -29,23 +29,23 @@ compatible = "gpio-keys"; menu_button: menu_button { label = "Menu"; - gpios = <&gpio0 13 GPIO_INT_ACTIVE_LOW>; + gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; }; select_button: select_button { label = "Select"; - gpios = <&gpio0 27 GPIO_INT_ACTIVE_LOW>; + gpios = <&gpio0 27 GPIO_ACTIVE_LOW>; }; a_button: a_button { label = "A"; - gpios = <&gpio1 0 GPIO_INT_ACTIVE_LOW>; + gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; }; b_button: b_button { label = "B"; - gpios = <&gpio1 1 GPIO_INT_ACTIVE_LOW>; + gpios = <&gpio1 1 GPIO_ACTIVE_LOW>; }; start_button: start_button { label = "Start"; - gpios = <&gpio1 7 GPIO_INT_ACTIVE_LOW>; + gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; }; }; diff --git a/drivers/gpio/Kconfig.esp32 b/drivers/gpio/Kconfig.esp32 index a31ae00fd15..446dc44418c 100644 --- a/drivers/gpio/Kconfig.esp32 +++ b/drivers/gpio/Kconfig.esp32 @@ -15,6 +15,14 @@ if GPIO_ESP32 config GPIO_ESP32_IRQ int "IRQ line for ESP32 GPIO pins" 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 bool "ESP32 GPIO (pins 0-31)" diff --git a/drivers/gpio/gpio_esp32.c b/drivers/gpio/gpio_esp32.c index 224901765f5..3d391b8cebf 100644 --- a/drivers/gpio/gpio_esp32.c +++ b/drivers/gpio/gpio_esp32.c @@ -20,23 +20,34 @@ #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 { /* gpio_driver_data needs to be first */ struct gpio_driver_data common; 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; + volatile u32_t *set_reg; + volatile u32_t *clear_reg; + volatile u32_t *input_reg; + volatile u32_t *output_reg; + volatile u32_t *irq_status_reg; + volatile u32_t *irq_ack_reg; int pin_offset; } port; @@ -44,97 +55,12 @@ struct gpio_esp32_data { 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, u32_t pin, int flags) { 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; int r; @@ -143,31 +69,52 @@ static int gpio_esp32_config(struct device *dev, int access_op, } /* 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) { return r; } - pinmux_pin_set(data->pinmux, pin, PIN_FUNC_GPIO); - 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); + /* Set pin function as GPIO */ + pinmux_pin_set(data->pinmux, io_pin, PIN_FUNC_GPIO); + + + 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) { - r = pinmux_pin_input_enable(data->pinmux, pin, - PINMUX_OUTPUT_ENABLED); - assert(r >= 0); - } else { - pinmux_pin_input_enable(data->pinmux, pin, + if (flags & GPIO_OUTPUT) { + + if (flags & GPIO_SINGLE_ENDED) { + if (flags & GPIO_LINE_OPEN_DRAIN) { + *reg |= GPIO_PIN_PAD_DRIVER; + } 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); - config_polarity(pin, flags); } - config_drive_strength(pin, flags); - - return config_interrupt(pin, flags); + return 0; } 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); if (value) { - *data->port.write.set_reg = v; + *data->port.set_reg = v; } else { - *data->port.write.clear_reg = v; + *data->port.clear_reg = v; } return 0; @@ -200,12 +147,147 @@ static int gpio_esp32_read(struct device *dev, int access_op, return -ENOTSUP; } - v = *data->port.read.reg; + v = *data->port.input_reg; *value = !!(v & BIT(pin - data->port.pin_offset)); 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, struct gpio_callback *callback, bool set) @@ -246,13 +328,12 @@ static int gpio_esp32_disable_callback(struct device *dev, 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; + u32_t values = *data->port.irq_status_reg; + *data->port.irq_ack_reg = values; 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); @@ -288,6 +369,12 @@ static const struct gpio_driver_api gpio_esp32_driver = { .config = gpio_esp32_config, .write = gpio_esp32_write, .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, .enable_callback = gpio_esp32_enable_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) 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, - }, + .set_reg = (u32_t *)GPIO_OUT_W1TS_REG, + .clear_reg = (u32_t *)GPIO_OUT_W1TC_REG, + .input_reg = (u32_t *)GPIO_IN_REG, + .output_reg = (u32_t *)GPIO_OUT_REG, + .irq_status_reg = (u32_t *)GPIO_STATUS_REG, + .irq_ack_reg = (u32_t *)GPIO_STATUS_W1TC_REG, .pin_offset = 0, } }; @@ -315,30 +397,25 @@ static struct gpio_esp32_data gpio_data_pins_0_to_31 = { #if defined(CONFIG_GPIO_ESP32_1) static struct gpio_esp32_data gpio_data_pins_32_to_39 = { .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, - }, + .set_reg = (u32_t *)GPIO_OUT1_W1TS_REG, + .clear_reg = (u32_t *)GPIO_OUT1_W1TC_REG, + .input_reg = (u32_t *)GPIO_IN1_REG, + .output_reg = (u32_t *)GPIO_OUT1_REG, + .irq_status_reg = (u32_t *)GPIO_STATUS1_REG, + .irq_ack_reg = (u32_t *)GPIO_STATUS1_W1TC_REG, .pin_offset = 32, } }; #endif -#define GPIO_DEVICE_INIT(__name, __data_struct_name) \ - DEVICE_AND_API_INIT(gpio_esp32_ ## __data_struct_name, \ - __name, \ - gpio_esp32_init, \ +#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, \ + NULL, \ + POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ &gpio_esp32_driver) /* 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) { + #if defined(CONFIG_GPIO_ESP32_0) gpio_esp32_fire_callbacks(DEVICE_GET(gpio_esp32_0_to_31)); #endif diff --git a/drivers/i2c/i2c_esp32.c b/drivers/i2c/i2c_esp32.c index 1bf57f7931c..be3eaa4cb73 100644 --- a/drivers/i2c/i2c_esp32.c +++ b/drivers/i2c/i2c_esp32.c @@ -98,9 +98,9 @@ struct i2c_esp32_config { static int i2c_esp32_configure_pins(int pin, int matrix_out, int matrix_in) { - const int pin_mode = GPIO_DIR_OUT | - GPIO_DS_DISCONNECT_LOW | - GPIO_PUD_PULL_UP; + const int pin_mode = GPIO_OUTPUT_HIGH | + GPIO_OPEN_DRAIN | + GPIO_PULL_UP; const char *device_name = gpio_esp32_get_gpio_for_pin(pin); struct device *gpio; int ret; @@ -118,11 +118,6 @@ static int i2c_esp32_configure_pins(int pin, int matrix_out, int matrix_in) 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_in(pin, matrix_in, false); diff --git a/drivers/pwm/pwm_led_esp32.c b/drivers/pwm/pwm_led_esp32.c index 27f39b719d1..1bbac4483e6 100644 --- a/drivers/pwm/pwm_led_esp32.c +++ b/drivers/pwm/pwm_led_esp32.c @@ -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, int duty, int timer) { - const int pin_mode = GPIO_DIR_OUT; + const int pin_mode = GPIO_OUTPUT; const char *device_name; struct device *gpio; diff --git a/tests/drivers/gpio/gpio_basic_api/boards/esp32.overlay b/tests/drivers/gpio/gpio_basic_api/boards/esp32.overlay new file mode 100644 index 00000000000..c1a308ec8ff --- /dev/null +++ b/tests/drivers/gpio/gpio_basic_api/boards/esp32.overlay @@ -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). + */