11/*
2- * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
2+ * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
33 *
44 * SPDX-License-Identifier: Apache-2.0
55 */
2727#include "driver/uart_select.h"
2828#include "esp_private/esp_clk_tree_common.h"
2929#include "esp_private/gpio.h"
30+ #include "esp_private/esp_gpio_reserve.h"
3031#include "esp_private/uart_share_hw_ctrl.h"
3132#include "esp_clk_tree.h"
3233#include "sdkconfig.h"
@@ -739,8 +740,19 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
739740 }
740741#endif
741742
743+ // Potential IO reserved mask
744+ uint64_t io_reserve_mask = 0 ;
745+ io_reserve_mask |= (tx_io_num > 0 ? BIT64 (tx_io_num ) : 0 );
746+ io_reserve_mask |= (rx_io_num > 0 ? BIT64 (rx_io_num ) : 0 );
747+ io_reserve_mask |= (rts_io_num > 0 ? BIT64 (rts_io_num ) : 0 );
748+ io_reserve_mask |= (cts_io_num > 0 ? BIT64 (cts_io_num ) : 0 );
749+
750+ // Since an IO cannot route peripheral signals via IOMUX and GPIO matrix at the same time,
751+ // if tx and rx share the same IO, both signals need to be route to IOs through GPIO matrix
752+ bool tx_rx_same_io = (tx_io_num == rx_io_num );
753+
742754 /* In the following statements, if the io_num is negative, no need to configure anything. */
743- if (tx_io_num >= 0 && !uart_try_set_iomux_pin (uart_num , tx_io_num , SOC_UART_TX_PIN_IDX )) {
755+ if (tx_io_num >= 0 && ( tx_rx_same_io || !uart_try_set_iomux_pin (uart_num , tx_io_num , SOC_UART_TX_PIN_IDX ) )) {
744756 if (uart_num < SOC_UART_HP_NUM ) {
745757 gpio_func_sel (tx_io_num , PIN_FUNC_GPIO );
746758 esp_rom_gpio_connect_out_signal (tx_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_TX_PIN_IDX ), 0 , 0 );
@@ -749,27 +761,27 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
749761 }
750762#if SOC_LP_GPIO_MATRIX_SUPPORTED
751763 else {
752- rtc_gpio_init (tx_io_num );
753- rtc_gpio_iomux_func_sel (tx_io_num , RTCIO_LL_PIN_FUNC );
754-
764+ rtc_gpio_init (tx_io_num ); // set as a LP_GPIO pin
755765 lp_gpio_connect_out_signal (tx_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_TX_PIN_IDX ), 0 , 0 );
756766 // output enable is set inside lp_gpio_connect_out_signal func after the signal is connected
757767 }
758768#endif
759769 }
760770
761- if (rx_io_num >= 0 && !uart_try_set_iomux_pin (uart_num , rx_io_num , SOC_UART_RX_PIN_IDX )) {
771+ if (rx_io_num >= 0 && (tx_rx_same_io || !uart_try_set_iomux_pin (uart_num , rx_io_num , SOC_UART_RX_PIN_IDX ))) {
772+ io_reserve_mask &= ~BIT64 (rx_io_num ); // input IO via GPIO matrix does not need to be reserved
762773 if (uart_num < SOC_UART_HP_NUM ) {
763774 gpio_func_sel (rx_io_num , PIN_FUNC_GPIO );
764- gpio_set_pull_mode (rx_io_num , GPIO_PULLUP_ONLY ); // This does not consider that RX signal can be read inverted by configuring the hardware (i.e. idle is at low level). However, it is only a weak pullup, the TX at the other end can always drive the line.
765- gpio_set_direction (rx_io_num , GPIO_MODE_INPUT );
775+ gpio_input_enable (rx_io_num );
766776 esp_rom_gpio_connect_in_signal (rx_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_RX_PIN_IDX ), 0 );
767777 }
768778#if SOC_LP_GPIO_MATRIX_SUPPORTED
769779 else {
770- rtc_gpio_set_direction (rx_io_num , RTC_GPIO_MODE_INPUT_ONLY );
771- rtc_gpio_init (rx_io_num );
772- rtc_gpio_iomux_func_sel (rx_io_num , RTCIO_LL_PIN_FUNC );
780+ rtc_gpio_mode_t mode = (tx_rx_same_io ? RTC_GPIO_MODE_INPUT_OUTPUT : RTC_GPIO_MODE_INPUT_ONLY );
781+ rtc_gpio_set_direction (rx_io_num , mode );
782+ if (!tx_rx_same_io ) { // set the same pin again as a LP_GPIO will overwrite connected out_signal, not desired, so skip
783+ rtc_gpio_init (rx_io_num ); // set as a LP_GPIO pin
784+ }
773785
774786 lp_gpio_connect_in_signal (rx_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_RX_PIN_IDX ), 0 );
775787 }
@@ -784,31 +796,39 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
784796 }
785797#if SOC_LP_GPIO_MATRIX_SUPPORTED
786798 else {
787- rtc_gpio_init (rts_io_num );
788- rtc_gpio_iomux_func_sel (rts_io_num , RTCIO_LL_PIN_FUNC );
799+ rtc_gpio_init (rts_io_num ); // set as a LP_GPIO pin
789800 lp_gpio_connect_out_signal (rts_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_RTS_PIN_IDX ), 0 , 0 );
790801 // output enable is set inside lp_gpio_connect_out_signal func after the signal is connected
791802 }
792803#endif
793804 }
794805
795806 if (cts_io_num >= 0 && !uart_try_set_iomux_pin (uart_num , cts_io_num , SOC_UART_CTS_PIN_IDX )) {
807+ io_reserve_mask &= ~BIT64 (cts_io_num ); // input IO via GPIO matrix does not need to be reserved
796808 if (uart_num < SOC_UART_HP_NUM ) {
797809 gpio_func_sel (cts_io_num , PIN_FUNC_GPIO );
798- gpio_set_pull_mode (cts_io_num , GPIO_PULLUP_ONLY );
799- gpio_set_direction (cts_io_num , GPIO_MODE_INPUT );
810+ gpio_pullup_en (cts_io_num );
811+ gpio_input_enable (cts_io_num );
800812 esp_rom_gpio_connect_in_signal (cts_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_CTS_PIN_IDX ), 0 );
801813 }
802814#if SOC_LP_GPIO_MATRIX_SUPPORTED
803815 else {
804816 rtc_gpio_set_direction (cts_io_num , RTC_GPIO_MODE_INPUT_ONLY );
805- rtc_gpio_init (cts_io_num );
806- rtc_gpio_iomux_func_sel (cts_io_num , RTCIO_LL_PIN_FUNC );
807-
817+ rtc_gpio_init (cts_io_num ); // set as a LP_GPIO pin
808818 lp_gpio_connect_in_signal (cts_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_CTS_PIN_IDX ), 0 );
809819 }
810820#endif
811821 }
822+
823+ // IO reserve
824+ uint64_t old_busy_mask = esp_gpio_reserve (io_reserve_mask );
825+ uint64_t conflict_mask = old_busy_mask & io_reserve_mask ;
826+ while (conflict_mask > 0 ) {
827+ uint8_t pos = __builtin_ctzll (conflict_mask );
828+ conflict_mask &= ~(1ULL << pos );
829+ ESP_LOGW (UART_TAG , "GPIO %d is not usable, maybe used by others" , pos );
830+ }
831+
812832 return ESP_OK ;
813833}
814834
@@ -1133,12 +1153,6 @@ static void UART_ISR_ATTR uart_rx_intr_handler_default(void *param)
11331153 uart_event .type = UART_DATA ;
11341154 uart_event .size = rx_fifo_len ;
11351155 uart_event .timeout_flag = (uart_intr_status & UART_INTR_RXFIFO_TOUT ) ? true : false;
1136- UART_ENTER_CRITICAL_ISR (& uart_selectlock );
1137- if (p_uart -> uart_select_notif_callback ) {
1138- p_uart -> uart_select_notif_callback (uart_num , UART_SELECT_READ_NOTIF , & HPTaskAwoken );
1139- need_yield |= (HPTaskAwoken == pdTRUE );
1140- }
1141- UART_EXIT_CRITICAL_ISR (& uart_selectlock );
11421156 }
11431157 p_uart -> rx_stash_len = rx_fifo_len ;
11441158 //If we fail to push data to ring buffer, we will have to stash the data, and send next time.
@@ -1187,6 +1201,15 @@ static void UART_ISR_ATTR uart_rx_intr_handler_default(void *param)
11871201 p_uart -> rx_buffered_len += p_uart -> rx_stash_len ;
11881202 UART_EXIT_CRITICAL_ISR (& (uart_context [uart_num ].spinlock ));
11891203 }
1204+
1205+ if (uart_event .type == UART_DATA ) {
1206+ UART_ENTER_CRITICAL_ISR (& uart_selectlock );
1207+ if (p_uart -> uart_select_notif_callback ) {
1208+ p_uart -> uart_select_notif_callback (uart_num , UART_SELECT_READ_NOTIF , & HPTaskAwoken );
1209+ need_yield |= (HPTaskAwoken == pdTRUE );
1210+ }
1211+ UART_EXIT_CRITICAL_ISR (& uart_selectlock );
1212+ }
11901213 } else {
11911214 UART_ENTER_CRITICAL_ISR (& (uart_context [uart_num ].spinlock ));
11921215 uart_hal_disable_intr_mask (& (uart_context [uart_num ].hal ), UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT );
0 commit comments