From c952cfb673ac1b2b0353abdb40cb6cab0faaea5a Mon Sep 17 00:00:00 2001 From: morris Date: Tue, 20 Feb 2024 15:20:33 +0800 Subject: [PATCH 1/2] feat(gpio): reserve gpio output atomically --- components/esp_driver_gpio/src/gpio.c | 4 +- components/esp_hw_support/esp_gpio_reserve.c | 27 +++++----- .../include/esp_private/esp_gpio_reserve.h | 49 ++++++++++++------- components/esp_hw_support/linker.lf | 2 - .../esp_psram/esp32/esp_psram_impl_quad.c | 26 +++++----- .../esp_psram/esp32s2/esp_psram_impl_quad.c | 26 +++++----- .../esp_psram/esp32s3/esp_psram_impl_octal.c | 4 +- .../esp_psram/esp32s3/esp_psram_impl_quad.c | 4 +- components/esp_psram/esp_psram_impl.h | 6 ++- components/spi_flash/flash_ops.c | 4 +- 10 files changed, 85 insertions(+), 67 deletions(-) diff --git a/components/esp_driver_gpio/src/gpio.c b/components/esp_driver_gpio/src/gpio.c index de667687cc10..a95fb74efdd4 100644 --- a/components/esp_driver_gpio/src/gpio.c +++ b/components/esp_driver_gpio/src/gpio.c @@ -46,7 +46,7 @@ typedef struct { typedef struct { int source; /*!< ISR source */ int intr_alloc_flags; /*!< ISR alloc flag */ - void (*fn)(void*); /*!< ISR function */ + void (*fn)(void *); /*!< ISR function */ void *arg; /*!< ISR function args*/ void *handle; /*!< ISR handle */ esp_err_t ret; @@ -1016,7 +1016,7 @@ esp_err_t gpio_dump_io_configuration(FILE *out_stream, uint64_t io_bit_mask) uint32_t drv, fun_sel, sig_out; gpio_hal_get_io_config(gpio_context.gpio_hal, gpio_num, &pu, &pd, &ie, &oe, &od, &drv, &fun_sel, &sig_out, &slp_sel); - fprintf(out_stream, "IO[%"PRIu32"]%s -\n", gpio_num, esp_gpio_is_pin_reserved(gpio_num) ? " **RESERVED**" : ""); + fprintf(out_stream, "IO[%"PRIu32"]%s -\n", gpio_num, esp_gpio_is_reserved(BIT64(gpio_num)) ? " **RESERVED**" : ""); fprintf(out_stream, " Pullup: %d, Pulldown: %d, DriveCap: %"PRIu32"\n", pu, pd, drv); fprintf(out_stream, " InputEn: %d, OutputEn: %d, OpenDrain: %d\n", ie, oe, od); fprintf(out_stream, " FuncSel: %"PRIu32" (%s)\n", fun_sel, (fun_sel == PIN_FUNC_GPIO) ? "GPIO" : "IOMUX"); diff --git a/components/esp_hw_support/esp_gpio_reserve.c b/components/esp_hw_support/esp_gpio_reserve.c index dba3893422d5..b7891c879d85 100644 --- a/components/esp_hw_support/esp_gpio_reserve.c +++ b/components/esp_hw_support/esp_gpio_reserve.c @@ -1,29 +1,30 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +#include +#include "soc/soc_caps.h" #include "esp_types.h" #include "esp_bit_defs.h" -#include "soc/soc_caps.h" +#include "esp_private/esp_gpio_reserve.h" + +static _Atomic uint64_t s_reserved_pin_mask = ATOMIC_VAR_INIT(~(SOC_GPIO_VALID_OUTPUT_GPIO_MASK)); -static uint64_t s_reserve_status = 0; +uint64_t esp_gpio_reserve(uint64_t gpio_mask) +{ + return atomic_fetch_or(&s_reserved_pin_mask, gpio_mask); +} -void esp_gpio_reserve_pins(uint64_t mask) +uint64_t esp_gpio_revoke(uint64_t gpio_mask) { -#if SOC_GPIO_PIN_COUNT < 64 - mask &= BIT64(SOC_GPIO_PIN_COUNT) - 1; -#endif - s_reserve_status |= mask; + return atomic_fetch_and(&s_reserved_pin_mask, ~gpio_mask); } -bool esp_gpio_is_pin_reserved(uint32_t gpio_num) +bool esp_gpio_is_reserved(uint64_t gpio_mask) { - if (gpio_num >= SOC_GPIO_PIN_COUNT) { - return false; - } - return !!(s_reserve_status & BIT64(gpio_num)); + return atomic_load(&s_reserved_pin_mask) & gpio_mask; } // TODO: IDF-6968 reserve the pins that not fanned out regarding the SiP version diff --git a/components/esp_hw_support/include/esp_private/esp_gpio_reserve.h b/components/esp_hw_support/include/esp_private/esp_gpio_reserve.h index 068386536aa6..6449613d0cd0 100644 --- a/components/esp_hw_support/include/esp_private/esp_gpio_reserve.h +++ b/components/esp_hw_support/include/esp_private/esp_gpio_reserve.h @@ -1,18 +1,25 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ + /** - * File Introduction: - * This file is used to reserve the GPIOs runtime, which has been occupied by FLASH/PSRAM or - * the GPIOs that not fan out. + * Background: * - * The FLASH pins can be tuned according to eFuse, pins will be reserved in the `esp_mspi_pin_init` - * while starting the CPU. + * - On some **modules**, specific GPIOs are connected to the PSRAM or Flash, and they shouldn't be used as general purpose IOs in the user's projects. + * - Some GPIO may be not fan-out in the SiP variant. + * - GPIO A is driven by peripheral M, we don't want peripheral N to use the same GPIO. + * - User may deliver a board where a GPIO is used for a special purpose or even not fan-out on the PCB, they want to reserve it in the BSP package. + * ... + */ + +/** + * Usage Attention: * - * As for the PSRAM pins, they are initialized after CPU started. They will be reserved in - * the `psram_gpio_config` when enabling the PSRAM. + * - If a GPIO is used by IO MUX, no matter it's used as Input or Output, we should reserve it, because IO MUX's different "FUNC" has its dedicated peripheral. + * - If a GPIO is used by Matrix, and only use its output path, we should reserve it, because we can't bind multiple peripheral output signals to the same GPIO. + * - When doing GPIO reserve, we must check its return value, to ensure the same GPIO is not reserved already. */ #pragma once @@ -24,22 +31,30 @@ extern "C" { #endif /** - * @brief Set the reserved pin - * @note A same gpio can be reserve repetitively, but can't be clear once it is reserved + * @brief Reserve the given GPIOs by mask, so they can't be used by others + * + * @param gpio_mask Mask of the GPIOs to be reserved + * @return The mask of the GPIOs that were already reserved before this call + */ +uint64_t esp_gpio_reserve(uint64_t gpio_mask); + +/** + * @brief Revoke the given GPIOs by mask, so they can be reused again by others * - * @param[in] mask Mask of GPIO reserved pins + * @param gpio_mask Mask of the GPIOs to be revoked + * @return The mask of the GPIOs that were already reserved before this call */ -void esp_gpio_reserve_pins(uint64_t mask); +uint64_t esp_gpio_revoke(uint64_t gpio_mask); /** - * @brief Check whether the pin has been reserved + * @brief Check whether the given GPIOs are reserved * - * @param[in] gpio_num GPIO pin number, please input a gpio number within `SOC_GPIO_PIN_COUNT` + * @param gpio_mask Mask of the GPIOs to be checked * @return - * - true This gpio is reserved for FLASH or PSRAM - * - false This gpio is available for other purposes + * - true Aay of the given GPIO(s) is reserved + * - false Aay of the given GPIO(s) is not reserved */ -bool esp_gpio_is_pin_reserved(uint32_t gpio_num); +bool esp_gpio_is_reserved(uint64_t gpio_mask); #ifdef __cplusplus } diff --git a/components/esp_hw_support/linker.lf b/components/esp_hw_support/linker.lf index ad3fd0d69aa7..0ad8003c5644 100644 --- a/components/esp_hw_support/linker.lf +++ b/components/esp_hw_support/linker.lf @@ -13,8 +13,6 @@ entries: cpu: esp_cpu_compare_and_set (noflash) esp_memory_utils (noflash) rtc_clk (noflash) - esp_gpio_reserve: esp_gpio_reserve_pins (noflash) - esp_gpio_reserve: esp_gpio_is_pin_reserved (noflash) if SOC_CONFIGURABLE_VDDSDIO_SUPPORTED = y: rtc_init:rtc_vddsdio_get_config (noflash) rtc_init:rtc_vddsdio_set_config (noflash) diff --git a/components/esp_psram/esp32/esp_psram_impl_quad.c b/components/esp_psram/esp32/esp_psram_impl_quad.c index e235ad5607f8..e7ca55f56605 100644 --- a/components/esp_psram/esp32/esp_psram_impl_quad.c +++ b/components/esp_psram/esp32/esp_psram_impl_quad.c @@ -1,13 +1,13 @@ /* - Driver bits for PSRAM chips (at the moment only the ESP-PSRAM32 chip). -*/ - -/* - * SPDX-FileCopyrightText: 2013-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2013-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +/* + Driver bits for PSRAM chips (at the moment only the ESP-PSRAM32 chip). +*/ + #include "sdkconfig.h" #include "string.h" #include "esp_attr.h" @@ -829,14 +829,14 @@ static void IRAM_ATTR psram_gpio_config(psram_io_t *psram_io, psram_cache_speed_ } // Reserve psram pins - esp_gpio_reserve_pins(BIT64(psram_io->flash_clk_io) | - BIT64(psram_io->flash_cs_io) | - BIT64(psram_io->psram_clk_io) | - BIT64(psram_io->psram_cs_io) | - BIT64(psram_io->psram_spiq_sd0_io) | - BIT64(psram_io->psram_spid_sd1_io) | - BIT64(psram_io->psram_spihd_sd2_io) | - BIT64(psram_io->psram_spiwp_sd3_io)); + esp_gpio_reserve(BIT64(psram_io->flash_clk_io) | + BIT64(psram_io->flash_cs_io) | + BIT64(psram_io->psram_clk_io) | + BIT64(psram_io->psram_cs_io) | + BIT64(psram_io->psram_spiq_sd0_io) | + BIT64(psram_io->psram_spid_sd1_io) | + BIT64(psram_io->psram_spihd_sd2_io) | + BIT64(psram_io->psram_spiwp_sd3_io)); } //used in UT only diff --git a/components/esp_psram/esp32s2/esp_psram_impl_quad.c b/components/esp_psram/esp32s2/esp_psram_impl_quad.c index b84905cffec4..414aaef470b9 100644 --- a/components/esp_psram/esp32s2/esp_psram_impl_quad.c +++ b/components/esp_psram/esp32s2/esp_psram_impl_quad.c @@ -1,13 +1,13 @@ /* - Driver bits for PSRAM chips (at the moment only the ESP-PSRAM32 chip). -*/ - -/* - * SPDX-FileCopyrightText: 2013-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2013-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +/* + Driver bits for PSRAM chips (at the moment only the ESP-PSRAM32 chip). +*/ + #include "sdkconfig.h" #include "string.h" #include "esp_attr.h" @@ -383,14 +383,14 @@ static void IRAM_ATTR psram_gpio_config(psram_cache_speed_t mode) s_psram_cs_io = psram_io.psram_cs_io; // Preserve psram pins - esp_gpio_reserve_pins(BIT64(psram_io.flash_clk_io) | - BIT64(psram_io.flash_cs_io) | - BIT64(psram_io.psram_clk_io) | - BIT64(psram_io.psram_cs_io) | - BIT64(psram_io.psram_spiq_sd0_io) | - BIT64(psram_io.psram_spid_sd1_io) | - BIT64(psram_io.psram_spihd_sd2_io) | - BIT64(psram_io.psram_spiwp_sd3_io)); + esp_gpio_reserve(BIT64(psram_io.flash_clk_io) | + BIT64(psram_io.flash_cs_io) | + BIT64(psram_io.psram_clk_io) | + BIT64(psram_io.psram_cs_io) | + BIT64(psram_io.psram_spiq_sd0_io) | + BIT64(psram_io.psram_spid_sd1_io) | + BIT64(psram_io.psram_spihd_sd2_io) | + BIT64(psram_io.psram_spiwp_sd3_io)); } //used in UT only diff --git a/components/esp_psram/esp32s3/esp_psram_impl_octal.c b/components/esp_psram/esp32s3/esp_psram_impl_octal.c index 218c039e06e2..42eeac475dcb 100644 --- a/components/esp_psram/esp32s3/esp_psram_impl_octal.c +++ b/components/esp_psram/esp32s3/esp_psram_impl_octal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -266,7 +266,7 @@ static void s_init_psram_pins(void) REG_SET_FIELD(SPI_MEM_DATE_REG(0), SPI_MEM_SPI_SMEM_SPICLK_FUN_DRV, 3); // Preserve psram pins - esp_gpio_reserve_pins(BIT64(OCT_PSRAM_CS1_IO)); + esp_gpio_reserve(BIT64(OCT_PSRAM_CS1_IO)); } /** diff --git a/components/esp_psram/esp32s3/esp_psram_impl_quad.c b/components/esp_psram/esp32s3/esp_psram_impl_quad.c index 27234062bfe6..52334cb62ba5 100644 --- a/components/esp_psram/esp32s3/esp_psram_impl_quad.c +++ b/components/esp_psram/esp32s3/esp_psram_impl_quad.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2013-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2013-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -299,7 +299,7 @@ static void psram_gpio_config(void) esp_rom_spiflash_select_qio_pins(wp_io, spiconfig); // Reserve psram pins - esp_gpio_reserve_pins(BIT64(cs1_io) | BIT64(wp_io)); + esp_gpio_reserve(BIT64(cs1_io) | BIT64(wp_io)); } esp_err_t esp_psram_impl_enable(void) //psram init diff --git a/components/esp_psram/esp_psram_impl.h b/components/esp_psram/esp_psram_impl.h index 956ec0b697a4..f5c25ccb90fb 100644 --- a/components/esp_psram/esp_psram_impl.h +++ b/components/esp_psram/esp_psram_impl.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -50,3 +50,7 @@ esp_err_t esp_psram_impl_enable(void); * @return psram CS IO */ uint8_t esp_psram_impl_get_cs_io(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index e2ba3b6ed947..db59405f633b 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -153,7 +153,7 @@ void IRAM_ATTR esp_mspi_pin_init(void) for (esp_mspi_io_t i = 0; i < ESP_MSPI_IO_MAX; i++) { reserve_pin_mask |= BIT64(esp_mspi_get_io(i)); } - esp_gpio_reserve_pins(reserve_pin_mask); + esp_gpio_reserve(reserve_pin_mask); } esp_err_t IRAM_ATTR spi_flash_init_chip_state(void) From 391a187c11e5bd2ec283ee2f4112c1f4ff1f2c73 Mon Sep 17 00:00:00 2001 From: morris Date: Tue, 20 Feb 2024 16:21:25 +0800 Subject: [PATCH 2/2] feat(rmt): check if the gpio number is reserved by others --- components/esp_driver_rmt/src/rmt_common.c | 1 - components/esp_driver_rmt/src/rmt_private.h | 1 + components/esp_driver_rmt/src/rmt_rx.c | 23 +++++++++------ components/esp_driver_rmt/src/rmt_tx.c | 32 ++++++++++++++------- 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/components/esp_driver_rmt/src/rmt_common.c b/components/esp_driver_rmt/src/rmt_common.c index b280fa93fa7e..5e404ee59af2 100644 --- a/components/esp_driver_rmt/src/rmt_common.c +++ b/components/esp_driver_rmt/src/rmt_common.c @@ -205,7 +205,6 @@ esp_err_t rmt_apply_carrier(rmt_channel_handle_t channel, const rmt_carrier_conf esp_err_t rmt_del_channel(rmt_channel_handle_t channel) { ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - gpio_reset_pin(channel->gpio_num); return channel->del(channel); } diff --git a/components/esp_driver_rmt/src/rmt_private.h b/components/esp_driver_rmt/src/rmt_private.h index 0915aae23ad8..647dffd877b7 100644 --- a/components/esp_driver_rmt/src/rmt_private.h +++ b/components/esp_driver_rmt/src/rmt_private.h @@ -24,6 +24,7 @@ #include "esp_pm.h" #include "esp_attr.h" #include "esp_private/gdma.h" +#include "esp_private/esp_gpio_reserve.h" #include "driver/rmt_common.h" #ifdef __cplusplus diff --git a/components/esp_driver_rmt/src/rmt_rx.c b/components/esp_driver_rmt/src/rmt_rx.c index 6dcf1d5a5101..1e8fec97dc31 100644 --- a/components/esp_driver_rmt/src/rmt_rx.c +++ b/components/esp_driver_rmt/src/rmt_rx.c @@ -146,6 +146,9 @@ static void rmt_rx_unregister_from_group(rmt_channel_t *channel, rmt_group_t *gr static esp_err_t rmt_rx_destroy(rmt_rx_channel_t *rx_channel) { + if (rx_channel->base.gpio_num >= 0) { + gpio_reset_pin(rx_channel->base.gpio_num); + } if (rx_channel->base.intr) { ESP_RETURN_ON_ERROR(esp_intr_free(rx_channel->base.intr), TAG, "delete interrupt service failed"); } @@ -177,21 +180,23 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_ rmt_rx_channel_t *rx_channel = NULL; // Check if priority is valid if (config->intr_priority) { - ESP_GOTO_ON_FALSE((config->intr_priority) > 0, ESP_ERR_INVALID_ARG, err, TAG, "invalid interrupt priority:%d", config->intr_priority); - ESP_GOTO_ON_FALSE(1 << (config->intr_priority) & RMT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err, TAG, "invalid interrupt priority:%d", config->intr_priority); + ESP_RETURN_ON_FALSE((config->intr_priority) > 0, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority); + ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & RMT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority); } - ESP_GOTO_ON_FALSE(config && ret_chan && config->resolution_hz, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "invalid GPIO number"); - ESP_GOTO_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL, - ESP_ERR_INVALID_ARG, err, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL); + ESP_RETURN_ON_FALSE(config && ret_chan && config->resolution_hz, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, TAG, "invalid GPIO number %d", config->gpio_num); + ESP_RETURN_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL, + ESP_ERR_INVALID_ARG, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL); #if !SOC_RMT_SUPPORT_DMA - ESP_GOTO_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "DMA not supported"); + ESP_RETURN_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA not supported"); #endif // SOC_RMT_SUPPORT_DMA // malloc channel memory uint32_t mem_caps = RMT_MEM_ALLOC_CAPS; rx_channel = heap_caps_calloc(1, sizeof(rmt_rx_channel_t), mem_caps); ESP_GOTO_ON_FALSE(rx_channel, ESP_ERR_NO_MEM, err, TAG, "no mem for rx channel"); + // gpio is not configured yet + rx_channel->base.gpio_num = -1; // create DMA descriptor size_t num_dma_nodes = 0; if (config->flags.with_dma) { @@ -266,16 +271,16 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_ #endif // GPIO Matrix/MUX configuration - rx_channel->base.gpio_num = config->gpio_num; gpio_config_t gpio_conf = { .intr_type = GPIO_INTR_DISABLE, // also enable the input path is `io_loop_back` is on, this is useful for debug .mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), .pull_down_en = false, .pull_up_en = true, - .pin_bit_mask = 1ULL << config->gpio_num, + .pin_bit_mask = BIT64(config->gpio_num), }; ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config GPIO failed"); + rx_channel->base.gpio_num = config->gpio_num; esp_rom_gpio_connect_in_signal(config->gpio_num, rmt_periph_signals.groups[group_id].channels[channel_id + RMT_RX_CHANNEL_OFFSET_IN_GROUP].rx_sig, config->flags.invert_in); diff --git a/components/esp_driver_rmt/src/rmt_tx.c b/components/esp_driver_rmt/src/rmt_tx.c index 5208f557eae9..81e019c3d3f0 100644 --- a/components/esp_driver_rmt/src/rmt_tx.c +++ b/components/esp_driver_rmt/src/rmt_tx.c @@ -185,6 +185,10 @@ static esp_err_t rmt_tx_create_trans_queue(rmt_tx_channel_t *tx_channel, const r static esp_err_t rmt_tx_destroy(rmt_tx_channel_t *tx_channel) { + if (tx_channel->base.gpio_num >= 0) { + gpio_reset_pin(tx_channel->base.gpio_num); + esp_gpio_revoke(BIT64(tx_channel->base.gpio_num)); + } if (tx_channel->base.intr) { ESP_RETURN_ON_ERROR(esp_intr_free(tx_channel->base.intr), TAG, "delete interrupt service failed"); } @@ -227,24 +231,26 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_ ESP_RETURN_ON_FALSE((config->intr_priority) > 0, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority); ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & RMT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority); } - ESP_GOTO_ON_FALSE(config && ret_chan && config->resolution_hz && config->trans_queue_depth, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "invalid GPIO number"); - ESP_GOTO_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL, - ESP_ERR_INVALID_ARG, err, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL); + ESP_RETURN_ON_FALSE(config && ret_chan && config->resolution_hz && config->trans_queue_depth, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, TAG, "invalid GPIO number %d", config->gpio_num); + ESP_RETURN_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL, + ESP_ERR_INVALID_ARG, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL); #if SOC_RMT_SUPPORT_DMA // we only support 2 nodes ping-pong, if the configured memory block size needs more than two DMA descriptors, should treat it as invalid - ESP_GOTO_ON_FALSE(config->mem_block_symbols <= RMT_DMA_DESC_BUF_MAX_SIZE * RMT_DMA_NODES_PING_PONG / sizeof(rmt_symbol_word_t), - ESP_ERR_INVALID_ARG, err, TAG, "mem_block_symbols can't exceed %d", - RMT_DMA_DESC_BUF_MAX_SIZE * RMT_DMA_NODES_PING_PONG / sizeof(rmt_symbol_word_t)); + ESP_RETURN_ON_FALSE(config->mem_block_symbols <= RMT_DMA_DESC_BUF_MAX_SIZE * RMT_DMA_NODES_PING_PONG / sizeof(rmt_symbol_word_t), + ESP_ERR_INVALID_ARG, TAG, "mem_block_symbols can't exceed %d", + RMT_DMA_DESC_BUF_MAX_SIZE * RMT_DMA_NODES_PING_PONG / sizeof(rmt_symbol_word_t)); #else - ESP_GOTO_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "DMA not supported"); + ESP_RETURN_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA not supported"); #endif // malloc channel memory uint32_t mem_caps = RMT_MEM_ALLOC_CAPS; tx_channel = heap_caps_calloc(1, sizeof(rmt_tx_channel_t) + sizeof(rmt_tx_trans_desc_t) * config->trans_queue_depth, mem_caps); ESP_GOTO_ON_FALSE(tx_channel, ESP_ERR_NO_MEM, err, TAG, "no mem for tx channel"); + // GPIO configuration is not done yet + tx_channel->base.gpio_num = -1; // create DMA descriptors if (config->flags.with_dma) { mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA; @@ -312,20 +318,26 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_ rmt_ll_tx_enable_wrap(hal->regs, channel_id, true); // GPIO Matrix/MUX configuration - tx_channel->base.gpio_num = config->gpio_num; gpio_config_t gpio_conf = { .intr_type = GPIO_INTR_DISABLE, // also enable the input path if `io_loop_back` is on, this is useful for bi-directional buses .mode = (config->flags.io_od_mode ? GPIO_MODE_OUTPUT_OD : GPIO_MODE_OUTPUT) | (config->flags.io_loop_back ? GPIO_MODE_INPUT : 0), .pull_down_en = false, .pull_up_en = true, - .pin_bit_mask = 1ULL << config->gpio_num, + .pin_bit_mask = BIT64(config->gpio_num), }; ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config GPIO failed"); + // reserve the GPIO output path, because we don't expect another peripheral to signal to the same GPIO + uint64_t old_gpio_rsv_mask = esp_gpio_reserve(BIT64(config->gpio_num)); + // check if the GPIO is already used by others, RMT TX channel only uses the output path of the GPIO + if (old_gpio_rsv_mask & BIT64(config->gpio_num)) { + ESP_LOGW(TAG, "GPIO %d is not usable, maybe conflict with others", config->gpio_num); + } esp_rom_gpio_connect_out_signal(config->gpio_num, rmt_periph_signals.groups[group_id].channels[channel_id + RMT_TX_CHANNEL_OFFSET_IN_GROUP].tx_sig, config->flags.invert_out, false); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->gpio_num], PIN_FUNC_GPIO); + tx_channel->base.gpio_num = config->gpio_num; portMUX_INITIALIZE(&tx_channel->base.spinlock); atomic_init(&tx_channel->base.fsm, RMT_FSM_INIT);