From 135f4f772adca71ec1a322a6a8c8a2d540f44db0 Mon Sep 17 00:00:00 2001 From: Glauber Maroto Ferreira Date: Sun, 30 Jan 2022 19:08:04 -0300 Subject: [PATCH] drivers: pinctrl: esp32: initial support add initial pinctrl driver support for ESP32. Signed-off-by: Glauber Maroto Ferreira --- drivers/pinctrl/CMakeLists.txt | 1 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Kconfig.esp32 | 11 + drivers/pinctrl/pinctrl_esp32.c | 286 ++++++++++++++++++ .../drivers/pinctrl/pinctrl_esp32_common.h | 31 ++ soc/xtensa/esp32/pinctrl_soc.h | 75 +++++ 6 files changed, 405 insertions(+) create mode 100644 drivers/pinctrl/Kconfig.esp32 create mode 100644 drivers/pinctrl/pinctrl_esp32.c create mode 100644 include/drivers/pinctrl/pinctrl_esp32_common.h create mode 100644 soc/xtensa/esp32/pinctrl_soc.h diff --git a/drivers/pinctrl/CMakeLists.txt b/drivers/pinctrl/CMakeLists.txt index 0d0fbe5a2ed..20c57d75439 100644 --- a/drivers/pinctrl/CMakeLists.txt +++ b/drivers/pinctrl/CMakeLists.txt @@ -18,3 +18,4 @@ zephyr_library_sources_ifdef(CONFIG_PINCTRL_MCUX_RT pinctrl_mcux_rt.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_SIFIVE pinctrl_sifive.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_NXP_IOCON pinctrl_lpc_iocon.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_CC13XX_CC26XX pinctrl_cc13xx_cc26xx.c) +zephyr_library_sources_ifdef(CONFIG_PINCTRL_ESP32 pinctrl_esp32.c) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 04964fa4df4..4caf25ee9bb 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -43,5 +43,6 @@ source "drivers/pinctrl/Kconfig.mcux" source "drivers/pinctrl/Kconfig.sifive" source "drivers/pinctrl/Kconfig.lpc_iocon" source "drivers/pinctrl/Kconfig.cc13xx_cc26xx" +source "drivers/pinctrl/Kconfig.esp32" endif # PINCTRL diff --git a/drivers/pinctrl/Kconfig.esp32 b/drivers/pinctrl/Kconfig.esp32 new file mode 100644 index 00000000000..bcbe8622c4b --- /dev/null +++ b/drivers/pinctrl/Kconfig.esp32 @@ -0,0 +1,11 @@ +# Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd. +# SPDX-License-Identifier: Apache-2.0 + +DT_COMPAT_ESP32_PINCTRL := espressif,esp32-pinctrl + +config PINCTRL_ESP32 + bool "Pin controller driver for ESP32 SoC" + depends on SOC_ESP32 + default $(dt_compat_enabled,$(DT_COMPAT_ESP32_PINCTRL)) + help + Enable pin controller driver for ESP32 SoC diff --git a/drivers/pinctrl/pinctrl_esp32.c b/drivers/pinctrl/pinctrl_esp32.c new file mode 100644 index 00000000000..261550fcf37 --- /dev/null +++ b/drivers/pinctrl/pinctrl_esp32.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Include esp-idf headers first to avoid redefining BIT() macro */ +#include +#include + +#include +#include +#include + +#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 +#endif + +#ifndef SOC_GPIO_SUPPORT_RTC_INDEPENDENT +#define SOC_GPIO_SUPPORT_RTC_INDEPENDENT 0 +#endif + +#define ESP32_INVALID_PORT_ADDR 0UL + +#define ESP32_GPIO_PORT_ADDR(nodelabel) \ + COND_CODE_1(DT_NODE_EXISTS(DT_NODELABEL(nodelabel)), \ + (DT_REG_ADDR(DT_NODELABEL(nodelabel)),), \ + (ESP32_INVALID_PORT_ADDR)) + +/** + * @brief Array containing each GPIO port address. + * + * Entries will be an invalid address if the port is not enabled. + */ +static const uint32_t esp32_gpio_ports_addrs[] = { + ESP32_GPIO_PORT_ADDR(gpio0) + ESP32_GPIO_PORT_ADDR(gpio1) +}; + +/** Number of GPIO ports. */ +static const size_t esp32_gpio_ports_cnt = ARRAY_SIZE(esp32_gpio_ports_addrs); + +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 esp32_pin_is_valid(uint32_t pin) +{ + return ((BIT(pin) & SOC_GPIO_VALID_GPIO_MASK) != 0); +} + +static inline bool esp32_pin_is_output_capable(uint32_t pin) +{ + return ((BIT(pin) & SOC_GPIO_VALID_OUTPUT_GPIO_MASK) != 0); +} + +static int esp32_pin_apply_config(uint32_t pin, uint32_t flags) +{ + gpio_dev_t *const gpio_base = (gpio_dev_t *)DT_REG_ADDR(DT_NODELABEL(gpio0)); + uint32_t io_pin = (uint32_t) pin + ((ESP32_PORT_IDX(pin) == 1 && pin < 32) ? 32 : 0); + int ret = 0; + + if (!esp32_pin_is_valid(io_pin)) { + return -EINVAL; + } + +#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) { + 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 & ESP32_PULL_UP_FLAG) { + 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 & ESP32_PULL_DOWN_FLAG) { + 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 & ESP32_DIR_OUT_FLAG) { + if (!esp32_pin_is_output_capable(pin)) { + ret = -EINVAL; + goto end; + } + + if (flags & ESP32_OPEN_DRAIN_FLAG) { + gpio_ll_od_enable(gpio_base, io_pin); + } else { + gpio_ll_od_disable(gpio_base, io_pin); + } + + /* Set output pin initial value */ + if (flags & ESP32_PIN_OUT_HIGH_FLAG) { + gpio_ll_set_level(gpio_base, io_pin, 1); + } else if (flags & ESP32_PIN_OUT_LOW_FLAG) { + gpio_ll_set_level(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); + } else { + gpio_ll_output_disable(&GPIO, io_pin); + } + + if (flags & ESP32_DIR_INP_FLAG) { + gpio_ll_input_enable(&GPIO, io_pin); + } else { + gpio_ll_input_disable(&GPIO, io_pin); + } + +end: + return ret; +} + +static int esp32_pin_configure(const uint32_t pin_mux, const uint32_t pin_cfg) +{ + uint32_t port_addr; + uint32_t pin_num = ESP32_PIN_NUM(pin_mux); + uint32_t sig_in = ESP32_PIN_SIGI(pin_mux); + uint32_t sig_out = ESP32_PIN_SIGO(pin_mux); + uint32_t flags = 0; + + if (ESP32_PORT_IDX(pin_num) >= esp32_gpio_ports_cnt) { + return -EINVAL; + } + + port_addr = esp32_gpio_ports_addrs[ESP32_PORT_IDX(pin_num)]; + + if (port_addr == ESP32_INVALID_PORT_ADDR) { + return -EINVAL; + } + + switch (ESP32_PIN_BIAS(pin_cfg)) { + case ESP32_PULL_UP: + flags |= ESP32_PULL_UP_FLAG; + break; + case ESP32_PULL_DOWN: + flags |= ESP32_PULL_DOWN_FLAG; + break; + default: + break; + } + + switch (ESP32_PIN_DRV(pin_cfg)) { + case ESP32_PUSH_PULL: + flags |= ESP32_PUSH_PULL_FLAG; + break; + case ESP32_OPEN_DRAIN: + flags |= ESP32_OPEN_DRAIN_FLAG; + break; + default: + break; + } + + if (sig_in == ESP_SIG_INVAL && sig_out == ESP_SIG_INVAL) { + return -ENOTSUP; + } + + if (sig_in != ESP_SIG_INVAL) { + flags |= ESP32_DIR_INP_FLAG; + } + + if (sig_out != ESP_SIG_INVAL) { + flags |= ESP32_DIR_OUT_FLAG; + } + + switch (ESP32_PIN_MODE_OUT(pin_cfg)) { + case ESP32_PIN_OUT_HIGH: + flags |= ESP32_PIN_OUT_HIGH_FLAG; + break; + case ESP32_PIN_OUT_LOW: + flags |= ESP32_PIN_OUT_LOW_FLAG; + break; + default: + break; + } + + if (flags & ESP32_PIN_OUT_HIGH_FLAG) { + if (ESP32_PORT_IDX(pin_num) == 0) { + gpio_dev_t *const gpio_dev = + (gpio_dev_t *)DT_REG_ADDR(DT_NODELABEL(gpio0)); + gpio_dev->out_w1ts = pin_num; +#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay) + } else { + gpio_dev_t *const gpio_dev = + (gpio_dev_t *)DT_REG_ADDR(DT_NODELABEL(gpio1)); + gpio_dev->out1_w1ts.data = pin_num; +#endif + } + } + + if (flags & ESP32_PIN_OUT_LOW_FLAG) { + if (ESP32_PORT_IDX(pin_num) == 0) { + gpio_dev_t *const gpio_dev = + (gpio_dev_t *)DT_REG_ADDR(DT_NODELABEL(gpio0)); + gpio_dev->out_w1tc = pin_num; +#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay) + } else { + gpio_dev_t *const gpio_dev = + (gpio_dev_t *)DT_REG_ADDR(DT_NODELABEL(gpio1)); + gpio_dev->out1_w1tc.data = pin_num; +#endif + } + } + + esp32_pin_apply_config(pin_num, flags); + + if (flags & ESP32_DIR_OUT_FLAG) { + esp_rom_gpio_matrix_out(pin_num, sig_out, 0, 0); + } + + if (flags & ESP32_DIR_INP_FLAG) { + esp_rom_gpio_matrix_in(pin_num, sig_in, 0); + } + + return 0; +} + +int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, + uintptr_t reg) +{ + uint32_t pin_mux, pin_cfg; + int ret = 0; + + ARG_UNUSED(reg); + + for (uint8_t i = 0U; i < pin_cnt; i++) { + pin_mux = pins[i].pinmux; + pin_cfg = pins[i].pincfg; + + ret = esp32_pin_configure(pin_mux, pin_cfg); + if (ret < 0) { + return ret; + } + } + + return 0; +} diff --git a/include/drivers/pinctrl/pinctrl_esp32_common.h b/include/drivers/pinctrl/pinctrl_esp32_common.h new file mode 100644 index 00000000000..3db2e5459d9 --- /dev/null +++ b/include/drivers/pinctrl/pinctrl_esp32_common.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_PINCTRL_PINCTRL_ESP32_COMMON_H_ +#define ZEPHYR_INCLUDE_DRIVERS_PINCTRL_PINCTRL_ESP32_COMMON_H_ + +#define ESP32_PORT_IDX(_pin) \ + (((_pin) < 32) ? 0 : 1) + +#define ESP32_PIN_NUM(_mux) \ + (((_mux) >> ESP32_PIN_NUM_SHIFT) & ESP32_PIN_NUM_MASK) + +#define ESP32_PIN_SIGI(_mux) \ + (((_mux) >> ESP32_PIN_SIGI_SHIFT) & ESP32_PIN_SIGI_MASK) + +#define ESP32_PIN_SIGO(_mux) \ + (((_mux) >> ESP32_PIN_SIGO_SHIFT) & ESP32_PIN_SIGO_MASK) + +#define ESP32_PIN_BIAS(_cfg) \ + (((_cfg) >> ESP32_PIN_BIAS_SHIFT) & ESP32_PIN_BIAS_MASK) + +#define ESP32_PIN_DRV(_cfg) \ + (((_cfg) >> ESP32_PIN_DRV_SHIFT) & ESP32_PIN_DRV_MASK) + +#define ESP32_PIN_MODE_OUT(_cfg) \ + (((_cfg) >> ESP32_PIN_OUT_SHIFT) & ESP32_PIN_OUT_MASK) + +#endif /* ZEPHYR_INCLUDE_DRIVERS_PINCTRL_PINCTRL_ESP32_COMMON_H_ */ diff --git a/soc/xtensa/esp32/pinctrl_soc.h b/soc/xtensa/esp32/pinctrl_soc.h new file mode 100644 index 00000000000..920088a2006 --- /dev/null +++ b/soc/xtensa/esp32/pinctrl_soc.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * ESP32 SoC specific helpers for pinctrl driver + */ + +#ifndef ZEPHYR_SOC_XTENSA_ESP32_PINCTRL_SOC_H_ +#define ZEPHYR_SOC_XTENSA_ESP32_PINCTRL_SOC_H_ + +#include +#include + +#include + +/** @cond INTERNAL_HIDDEN */ + +/** Type for ESP32 pin. */ +typedef struct pinctrl_soc_pin { + /** Pinmux settings (pin, direction and signal). */ + uint32_t pinmux; + /** Pincfg settings (bias). */ + uint32_t pincfg; +} pinctrl_soc_pin_t; + +/** + * @brief Utility macro to initialize pinmux field in #pinctrl_pin_t. + * + * @param node_id Node identifier. + */ +#define Z_PINCTRL_ESP32_PINMUX_INIT(node_id) DT_PROP(node_id, pinmux) + +/** + * @brief Utility macro to initialize pincfg field in #pinctrl_pin_t. + * + * @param node_id Node identifier. + */ +#define Z_PINCTRL_ESP32_PINCFG_INIT(node_id) \ + (((ESP32_NO_PULL * DT_PROP(node_id, bias_disable)) << ESP32_PIN_BIAS_SHIFT) | \ + ((ESP32_PULL_UP * DT_PROP(node_id, bias_pull_up)) << ESP32_PIN_BIAS_SHIFT) | \ + ((ESP32_PULL_DOWN * DT_PROP(node_id, bias_pull_down)) << ESP32_PIN_BIAS_SHIFT) | \ + ((ESP32_PUSH_PULL * DT_PROP(node_id, drive_push_pull)) << ESP32_PIN_DRV_SHIFT) | \ + ((ESP32_OPEN_DRAIN * DT_PROP(node_id, drive_open_drain)) << ESP32_PIN_DRV_SHIFT) | \ + ((ESP32_PIN_OUT_HIGH * DT_PROP(node_id, output_high)) << ESP32_PIN_OUT_SHIFT) | \ + ((ESP32_PIN_OUT_LOW * DT_PROP(node_id, output_low)) << ESP32_PIN_OUT_SHIFT)) + +/** + * @brief Utility macro to initialize each pin. + * + * @param node_id Node identifier. + * @param state_prop State property name. + * @param idx State property entry index. + */ +#define Z_PINCTRL_STATE_PIN_INIT(node_id, state_prop, idx) \ + { .pinmux = Z_PINCTRL_ESP32_PINMUX_INIT( \ + DT_PROP_BY_IDX(node_id, state_prop, idx)), \ + .pincfg = Z_PINCTRL_ESP32_PINCFG_INIT( \ + DT_PROP_BY_IDX(node_id, state_prop, idx)) }, + +/** + * @brief Utility macro to initialize state pins contained in a given property. + * + * @param node_id Node identifier. + * @param prop Property name describing state pins. + */ +#define Z_PINCTRL_STATE_PINS_INIT(node_id, prop) \ + {DT_FOREACH_PROP_ELEM(node_id, prop, Z_PINCTRL_STATE_PIN_INIT)} + +/** @endcond */ + +#endif /* ZEPHYR_SOC_XTENSA_ESP32_PINCTRL_SOC_H_ */