feat(uart): add pin release process to uart driver

This commit is contained in:
Song Ruo Jing
2025-04-23 15:28:41 +08:00
parent 606ba21345
commit 60f5828a4a
2 changed files with 125 additions and 38 deletions

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -32,6 +32,9 @@
#include "driver/rtc_io.h" #include "driver/rtc_io.h"
#include "soc/rtc_io_channel.h" #include "soc/rtc_io_channel.h"
#include "driver/lp_io.h" #include "driver/lp_io.h"
#if SOC_LP_GPIO_MATRIX_SUPPORTED
#include "soc/lp_gpio_pins.h"
#endif
#endif #endif
#if I2C_USE_RETENTION_LINK #if I2C_USE_RETENTION_LINK
#include "esp_private/sleep_retention.h" #include "esp_private/sleep_retention.h"
@ -427,8 +430,8 @@ esp_err_t i2c_common_deinit_pins(i2c_bus_handle_t handle)
ESP_RETURN_ON_ERROR(rtc_gpio_deinit(handle->sda_num), TAG, "deinit rtc gpio failed"); ESP_RETURN_ON_ERROR(rtc_gpio_deinit(handle->sda_num), TAG, "deinit rtc gpio failed");
ESP_RETURN_ON_ERROR(rtc_gpio_deinit(handle->scl_num), TAG, "deinit rtc gpio failed"); ESP_RETURN_ON_ERROR(rtc_gpio_deinit(handle->scl_num), TAG, "deinit rtc gpio failed");
#if SOC_LP_GPIO_MATRIX_SUPPORTED #if SOC_LP_GPIO_MATRIX_SUPPORTED
lp_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].scl_in_sig, 0); lp_gpio_connect_in_signal(LP_GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].scl_in_sig, 0);
lp_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].sda_in_sig, 0); lp_gpio_connect_in_signal(LP_GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].sda_in_sig, 0);
#endif #endif
} }
#endif #endif

View File

@ -36,6 +36,9 @@
#include "driver/rtc_io.h" #include "driver/rtc_io.h"
#include "hal/rtc_io_ll.h" #include "hal/rtc_io_ll.h"
#include "driver/lp_io.h" #include "driver/lp_io.h"
#if SOC_LP_GPIO_MATRIX_SUPPORTED
#include "soc/lp_gpio_pins.h"
#endif
#endif #endif
#include "clk_ctrl_os.h" #include "clk_ctrl_os.h"
#include "esp_pm.h" #include "esp_pm.h"
@ -109,6 +112,11 @@ static const char *UART_TAG = "uart";
.hal.dev = UART_LL_GET_HW(uart_num), \ .hal.dev = UART_LL_GET_HW(uart_num), \
INIT_CRIT_SECTION_LOCK_IN_STRUCT(spinlock) \ INIT_CRIT_SECTION_LOCK_IN_STRUCT(spinlock) \
.hw_enabled = false, \ .hw_enabled = false, \
.tx_io_num = -1, \
.rx_io_num = -1, \
.rts_io_num = -1, \
.cts_io_num = -1, \
.io_reserved_mask = 0, \
} }
typedef struct { typedef struct {
@ -170,6 +178,11 @@ typedef struct {
uart_hal_context_t hal; /*!< UART hal context*/ uart_hal_context_t hal; /*!< UART hal context*/
DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(spinlock) DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(spinlock)
bool hw_enabled; bool hw_enabled;
int tx_io_num;
int rx_io_num;
int rts_io_num;
int cts_io_num;
uint64_t io_reserved_mask;
} uart_context_t; } uart_context_t;
static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0};
@ -707,8 +720,66 @@ static bool uart_try_set_iomux_pin(uart_port_t uart_num, int io_num, uint32_t id
return true; return true;
} }
//internal signal can be output to multiple GPIO pads static void uart_release_pin(uart_port_t uart_num)
//only one GPIO pad can connect with input signal {
if (uart_num >= UART_NUM_MAX) {
return;
}
if (uart_context[uart_num].tx_io_num >= 0) {
gpio_output_disable(uart_context[uart_num].tx_io_num);
#if (SOC_UART_LP_NUM >= 1)
if (!(uart_num < SOC_UART_HP_NUM)) {
rtc_gpio_deinit(uart_context[uart_num].tx_io_num);
}
#endif
}
if (uart_context[uart_num].rx_io_num >= 0) {
if (uart_num < SOC_UART_HP_NUM) {
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false);
}
#if (SOC_UART_LP_NUM >= 1)
else {
#if SOC_LP_GPIO_MATRIX_SUPPORTED
lp_gpio_connect_in_signal(LP_GPIO_MATRIX_CONST_ONE_INPUT, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false);
#endif
rtc_gpio_deinit(uart_context[uart_num].rx_io_num);
}
#endif
}
if (uart_context[uart_num].rts_io_num >= 0) {
gpio_output_disable(uart_context[uart_num].rts_io_num);
#if (SOC_UART_LP_NUM >= 1)
if (!(uart_num < SOC_UART_HP_NUM)) {
rtc_gpio_deinit(uart_context[uart_num].rts_io_num);
}
#endif
}
if (uart_context[uart_num].cts_io_num >= 0) {
if (uart_num < SOC_UART_HP_NUM) {
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), false);
}
#if (SOC_UART_LP_NUM >= 1)
else {
#if SOC_LP_GPIO_MATRIX_SUPPORTED
lp_gpio_connect_in_signal(LP_GPIO_MATRIX_CONST_ZERO_INPUT, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), false);
#endif
rtc_gpio_deinit(uart_context[uart_num].cts_io_num);
}
#endif
}
esp_gpio_revoke(uart_context[uart_num].io_reserved_mask);
uart_context[uart_num].tx_io_num = -1;
uart_context[uart_num].rx_io_num = -1;
uart_context[uart_num].rts_io_num = -1;
uart_context[uart_num].cts_io_num = -1;
uart_context[uart_num].io_reserved_mask = 0;
}
esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num)
{ {
ESP_RETURN_ON_FALSE((uart_num >= 0), ESP_FAIL, UART_TAG, "uart_num error"); ESP_RETURN_ON_FALSE((uart_num >= 0), ESP_FAIL, UART_TAG, "uart_num error");
@ -740,6 +811,9 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
} }
#endif #endif
// First, release previously configured IOs if there is
uart_release_pin(uart_num);
// Potential IO reserved mask // Potential IO reserved mask
uint64_t io_reserve_mask = 0; uint64_t io_reserve_mask = 0;
io_reserve_mask |= (tx_io_num > 0 ? BIT64(tx_io_num) : 0); io_reserve_mask |= (tx_io_num > 0 ? BIT64(tx_io_num) : 0);
@ -752,42 +826,48 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
bool tx_rx_same_io = (tx_io_num == rx_io_num); bool tx_rx_same_io = (tx_io_num == rx_io_num);
/* In the following statements, if the io_num is negative, no need to configure anything. */ /* In the following statements, if the io_num is negative, no need to configure anything. */
if (tx_io_num >= 0 && (tx_rx_same_io || !uart_try_set_iomux_pin(uart_num, tx_io_num, SOC_UART_TX_PIN_IDX))) { if (tx_io_num >= 0) {
if (uart_num < SOC_UART_HP_NUM) { uart_context[uart_num].tx_io_num = tx_io_num;
gpio_func_sel(tx_io_num, PIN_FUNC_GPIO); if (tx_rx_same_io || !uart_try_set_iomux_pin(uart_num, tx_io_num, SOC_UART_TX_PIN_IDX)) {
esp_rom_gpio_connect_out_signal(tx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX), 0, 0); if (uart_num < SOC_UART_HP_NUM) {
// output enable is set inside esp_rom_gpio_connect_out_signal func after the signal is connected gpio_func_sel(tx_io_num, PIN_FUNC_GPIO);
// (output enabled too early may cause unnecessary level change at the pad) esp_rom_gpio_connect_out_signal(tx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX), 0, 0);
} // output enable is set inside esp_rom_gpio_connect_out_signal func after the signal is connected
#if SOC_LP_GPIO_MATRIX_SUPPORTED // (output enabled too early may cause unnecessary level change at the pad)
else { }
rtc_gpio_init(tx_io_num); // set as a LP_GPIO pin #if SOC_LP_GPIO_MATRIX_SUPPORTED
lp_gpio_connect_out_signal(tx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX), 0, 0); else {
// output enable is set inside lp_gpio_connect_out_signal func after the signal is connected rtc_gpio_init(tx_io_num); // set as a LP_GPIO pin
} lp_gpio_connect_out_signal(tx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX), 0, 0);
#endif // output enable is set inside lp_gpio_connect_out_signal func after the signal is connected
}
if (rx_io_num >= 0 && (tx_rx_same_io || !uart_try_set_iomux_pin(uart_num, rx_io_num, SOC_UART_RX_PIN_IDX))) {
io_reserve_mask &= ~BIT64(rx_io_num); // input IO via GPIO matrix does not need to be reserved
if (uart_num < SOC_UART_HP_NUM) {
gpio_input_enable(rx_io_num);
esp_rom_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0);
}
#if SOC_LP_GPIO_MATRIX_SUPPORTED
else {
rtc_gpio_mode_t mode = (tx_rx_same_io ? RTC_GPIO_MODE_INPUT_OUTPUT : RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_set_direction(rx_io_num, mode);
if (!tx_rx_same_io) { // set the same pin again as a LP_GPIO will overwrite connected out_signal, not desired, so skip
rtc_gpio_init(rx_io_num); // set as a LP_GPIO pin
} }
lp_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0);
}
#endif #endif
}
} }
if (rts_io_num >= 0 && !uart_try_set_iomux_pin(uart_num, rts_io_num, SOC_UART_RTS_PIN_IDX)) { if (rx_io_num >= 0) {
uart_context[uart_num].rx_io_num = rx_io_num;
if (tx_rx_same_io || !uart_try_set_iomux_pin(uart_num, rx_io_num, SOC_UART_RX_PIN_IDX)) {
io_reserve_mask &= ~BIT64(rx_io_num); // input IO via GPIO matrix does not need to be reserved
if (uart_num < SOC_UART_HP_NUM) {
gpio_input_enable(rx_io_num);
esp_rom_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0);
}
#if SOC_LP_GPIO_MATRIX_SUPPORTED
else {
rtc_gpio_mode_t mode = (tx_rx_same_io ? RTC_GPIO_MODE_INPUT_OUTPUT : RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_set_direction(rx_io_num, mode);
if (!tx_rx_same_io) { // set the same pin again as a LP_GPIO will overwrite connected out_signal, not desired, so skip
rtc_gpio_init(rx_io_num); // set as a LP_GPIO pin
}
lp_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0);
}
#endif
}
}
if (rts_io_num >= 0 && (uart_context[uart_num].rts_io_num = rts_io_num, !uart_try_set_iomux_pin(uart_num, rts_io_num, SOC_UART_RTS_PIN_IDX))) {
if (uart_num < SOC_UART_HP_NUM) { if (uart_num < SOC_UART_HP_NUM) {
gpio_func_sel(rts_io_num, PIN_FUNC_GPIO); gpio_func_sel(rts_io_num, PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(rts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RTS_PIN_IDX), 0, 0); esp_rom_gpio_connect_out_signal(rts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RTS_PIN_IDX), 0, 0);
@ -802,7 +882,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
#endif #endif
} }
if (cts_io_num >= 0 && !uart_try_set_iomux_pin(uart_num, cts_io_num, SOC_UART_CTS_PIN_IDX)) { if (cts_io_num >= 0 && (uart_context[uart_num].cts_io_num = cts_io_num, !uart_try_set_iomux_pin(uart_num, cts_io_num, SOC_UART_CTS_PIN_IDX))) {
io_reserve_mask &= ~BIT64(cts_io_num); // input IO via GPIO matrix does not need to be reserved io_reserve_mask &= ~BIT64(cts_io_num); // input IO via GPIO matrix does not need to be reserved
if (uart_num < SOC_UART_HP_NUM) { if (uart_num < SOC_UART_HP_NUM) {
gpio_pullup_en(cts_io_num); gpio_pullup_en(cts_io_num);
@ -819,6 +899,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
} }
// IO reserve // IO reserve
uart_context[uart_num].io_reserved_mask = io_reserve_mask;
uint64_t old_busy_mask = esp_gpio_reserve(io_reserve_mask); uint64_t old_busy_mask = esp_gpio_reserve(io_reserve_mask);
uint64_t conflict_mask = old_busy_mask & io_reserve_mask; uint64_t conflict_mask = old_busy_mask & io_reserve_mask;
while (conflict_mask > 0) { while (conflict_mask > 0) {
@ -1801,6 +1882,9 @@ esp_err_t uart_driver_delete(uart_port_t uart_num)
ESP_LOGI(UART_TAG, "ALREADY NULL"); ESP_LOGI(UART_TAG, "ALREADY NULL");
return ESP_OK; return ESP_OK;
} }
uart_release_pin(uart_num);
esp_intr_free(p_uart_obj[uart_num]->intr_handle); esp_intr_free(p_uart_obj[uart_num]->intr_handle);
uart_disable_rx_intr(uart_num); uart_disable_rx_intr(uart_num);
uart_disable_tx_intr(uart_num); uart_disable_tx_intr(uart_num);