diff --git a/components/driver/test_apps/uart/main/test_uart_auto_lightsleep.c b/components/driver/test_apps/uart/main/test_uart_auto_lightsleep.c new file mode 100644 index 0000000000..2cc24559f8 --- /dev/null +++ b/components/driver/test_apps/uart/main/test_uart_auto_lightsleep.c @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" +#include "unity.h" +#include "driver/uart.h" +#include "esp_pm.h" +#include "esp_log.h" + +#define UART_TAG "Uart" +#define TEST_BUF_SIZE 256 + +//This should be larger than FIFO_SIZE + 2 * TEST_DRIVER_BUF_SIZE, so that blocking will happen +#define TEST_WRITE_SIZE 1024 + +#define TEST_TXD 12 +#define TEST_RXD 13 +#define TEST_RTS UART_PIN_NO_CHANGE +#define TEST_CTS UART_PIN_NO_CHANGE + +#define TEST_UART_PORT_NUM 1 +#define TEST_UART_BAUD_RATE 115200 + +#define MAX_FREQ (CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ) + +#if CONFIG_XTAL_FREQ_40 +#define MIN_FREQ 10 +#elif CONFIG_XTAL_FREQ_32 +#define MIN_FREQ 8 +#elif CONFIG_XTAL_FREQ_26 +#define MIN_FREQ 13 +#endif + +#if CONFIG_PM_ENABLE + +TEST_CASE("uart tx won't be blocked by auto light sleep", "[uart]") +{ + // Configure dynamic frequency scaling: + // maximum and minimum frequencies are set in sdkconfig, + // automatic light sleep is enabled if tickless idle support is enabled. + esp_pm_config_t pm_config = { + .max_freq_mhz = MAX_FREQ, + .min_freq_mhz = MIN_FREQ, +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE + .light_sleep_enable = true +#endif + }; + TEST_ESP_OK(esp_pm_configure(&pm_config)); + + uart_config_t uart_config = { + .baud_rate = TEST_UART_BAUD_RATE, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT, + }; + int intr_alloc_flags = 0; + + TEST_ESP_OK(uart_driver_install(TEST_UART_PORT_NUM, TEST_BUF_SIZE, 0, 0, NULL, intr_alloc_flags)); + TEST_ESP_OK(uart_param_config(TEST_UART_PORT_NUM, &uart_config)); + TEST_ESP_OK(uart_set_pin(TEST_UART_PORT_NUM, TEST_TXD, TEST_RXD, TEST_RTS, TEST_CTS)); + + // Configure a temporary buffer for the incoming data + const int len = TEST_WRITE_SIZE; + uint8_t *data = (uint8_t *) malloc(len); + + //If auto lightsleep happen, there will be deadlock in either one of the two following functions + uart_write_bytes(TEST_UART_PORT_NUM, (const char *) data, len); + uart_wait_tx_done(TEST_UART_PORT_NUM, portMAX_DELAY); + + ESP_LOGI(UART_TAG, "return from uart_write_bytes"); + + uart_driver_delete(TEST_UART_PORT_NUM); + free(data); +} +#endif // CONFIG_PM_ENABLE diff --git a/components/driver/uart/uart.c b/components/driver/uart/uart.c index c72f5f33f5..e50f089cb3 100644 --- a/components/driver/uart/uart.c +++ b/components/driver/uart/uart.c @@ -28,6 +28,7 @@ #include "sdkconfig.h" #include "esp_rom_gpio.h" #include "clk_ctrl_os.h" +#include "esp_pm.h" #ifdef CONFIG_UART_ISR_IN_IRAM #define UART_ISR_ATTR IRAM_ATTR @@ -37,6 +38,14 @@ #define UART_MALLOC_CAPS MALLOC_CAP_DEFAULT #endif +// Whether to use the APB_MAX lock. +// Requirement of each chip, to keep sending: +// - ESP32, S2, C3, S3: Protect APB, which is the core clock and clock of UART FIFO +// - ESP32-C2: Protect APB (UART FIFO clock), core clock (TODO: IDF-8348) +// - ESP32-C6, H2 and later chips: Protect core clock. Run in light-sleep hasn't been developed yet (TODO: IDF-8349), +// also need to avoid auto light-sleep. +#define PROTECT_APB (CONFIG_PM_ENABLE) + #define XOFF (0x13) #define XON (0x11) @@ -144,6 +153,9 @@ typedef struct { void *tx_done_sem_struct; void *tx_brk_sem_struct; #endif +#if PROTECT_APB + esp_pm_lock_handle_t pm_lock; ///< Power management lock +#endif } uart_obj_t; typedef struct { @@ -1141,15 +1153,19 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) } else { ticks_to_wait = ticks_to_wait - (ticks_end - ticks_start); } + +#if PROTECT_APB + esp_pm_lock_acquire(p_uart_obj[uart_num]->pm_lock); +#endif //take 2nd tx_done_sem, wait given from ISR res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (TickType_t)ticks_to_wait); - if (res == pdFALSE) { - // The TX_DONE interrupt will be disabled in ISR - xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); - return ESP_ERR_TIMEOUT; - } +#if PROTECT_APB + esp_pm_lock_release(p_uart_obj[uart_num]->pm_lock); +#endif + + // The TX_DONE interrupt will be disabled in ISR xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); - return ESP_OK; + return (res == pdFALSE) ? ESP_ERR_TIMEOUT : ESP_OK; } int uart_tx_chars(uart_port_t uart_num, const char *buffer, uint32_t len) @@ -1176,6 +1192,9 @@ static int uart_tx_all(uart_port_t uart_num, const char *src, size_t size, bool //lock for uart_tx xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (TickType_t)portMAX_DELAY); +#if PROTECT_APB + esp_pm_lock_acquire(p_uart_obj[uart_num]->pm_lock); +#endif p_uart_obj[uart_num]->coll_det_flg = false; if (p_uart_obj[uart_num]->tx_buf_size > 0) { size_t max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); @@ -1219,6 +1238,9 @@ static int uart_tx_all(uart_port_t uart_num, const char *src, size_t size, bool } xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); } +#if PROTECT_APB + esp_pm_lock_release(p_uart_obj[uart_num]->pm_lock); +#endif xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return original_size; } @@ -1413,6 +1435,11 @@ static void uart_free_driver_obj(uart_obj_t *uart_obj) free(uart_obj->tx_brk_sem_struct); free(uart_obj->tx_done_sem_struct); free(uart_obj->tx_fifo_sem_struct); +#endif +#if PROTECT_APB + if (uart_obj->pm_lock) { + esp_pm_lock_delete(uart_obj->pm_lock); + } #endif free(uart_obj); } @@ -1498,6 +1525,12 @@ static uart_obj_t *uart_alloc_driver_obj(int event_queue_size, int tx_buffer_siz !uart_obj->tx_done_sem || !uart_obj->tx_fifo_sem) { goto err; } +#endif +#if PROTECT_APB + if (esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "uart_driver", + &uart_obj->pm_lock) != ESP_OK) { + goto err; + } #endif return uart_obj;