diff --git a/components/driver/test_apps/uart/main/CMakeLists.txt b/components/driver/test_apps/uart/main/CMakeLists.txt index e4d03ea3f7..a12c839914 100644 --- a/components/driver/test_apps/uart/main/CMakeLists.txt +++ b/components/driver/test_apps/uart/main/CMakeLists.txt @@ -1,7 +1,8 @@ # In order for the cases defined by `TEST_CASE` to be linked into the final elf, # the component can be registered as WHOLE_ARCHIVE idf_component_register( - SRCS "test_app_main.c" "test_uart.c" - REQUIRES driver unity + SRCS "test_app_main.c" "test_uart.c" "test_uart_auto_lightsleep.c" + REQUIRES driver unity esp_pm + PRIV_INCLUDE_DIRS . WHOLE_ARCHIVE ) 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..3c6da6db9f --- /dev/null +++ b/components/driver/test_apps/uart/main/test_uart_auto_lightsleep.c @@ -0,0 +1,104 @@ +/* + * 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" +#include "esp_private/sleep_cpu.h" //for sleep_cpu_configure + +#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_RTS UART_PIN_NO_CHANGE +#define TEST_CTS UART_PIN_NO_CHANGE + +#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 + +typedef struct { + uart_port_t port_num; + soc_module_clk_t default_src_clk; + int tx_pin_num; + int rx_pin_num; + uint32_t rx_flow_ctrl_thresh; +} uart_port_param_t; + + +#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32P4) +#if CONFIG_PM_ENABLE + +TEST_CASE("uart tx won't be blocked by auto light sleep", "[uart]") +{ + uart_port_param_t port_param = { + .port_num = UART_NUM_1, // Test HP_UART with UART1 port + .default_src_clk = UART_SCLK_DEFAULT, + .tx_pin_num = 4, + .rx_pin_num = 5, + .rx_flow_ctrl_thresh = 120, + }; + + uart_port_t port_num = port_param.port_num; + + // 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 = port_param.default_src_clk, + }; + int intr_alloc_flags = 0; + + TEST_ESP_OK(uart_driver_install(port_num, TEST_BUF_SIZE, 0, 0, NULL, intr_alloc_flags)); + TEST_ESP_OK(uart_param_config(port_num, &uart_config)); + TEST_ESP_OK(uart_set_pin(port_num, port_param.tx_pin_num, port_param.rx_pin_num, 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(port_num, (const char *) data, len); + uart_wait_tx_done(port_num, portMAX_DELAY); + + ESP_LOGI(UART_TAG, "return from uart_write_bytes"); + + uart_driver_delete(port_num); + free(data); + +#if CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP + //When PD_CPU enabled, retention may cause 14K memory leak. Workaround to release the memory + sleep_cpu_configure(false); +#endif +} +#endif // CONFIG_PM_ENABLE +#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32P4) 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; diff --git a/components/soc/include/soc/uart_periph.h b/components/soc/include/soc/uart_periph.h index 320b947c3b..df0b7e371c 100644 --- a/components/soc/include/soc/uart_periph.h +++ b/components/soc/include/soc/uart_periph.h @@ -1,16 +1,8 @@ -// Copyright 2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +/* + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once #include "soc/soc_caps.h" @@ -20,6 +12,8 @@ #include "soc/gpio_sig_map.h" #include "soc/io_mux_reg.h" #include "soc/uart_pins.h" +#include "soc/uart_struct.h" +#include "soc/uart_reg.h" #ifdef __cplusplus extern "C" { diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 7049092170..ad0eb8d822 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -964,7 +964,6 @@ components/soc/include/soc/ledc_periph.h components/soc/include/soc/sdio_slave_periph.h components/soc/include/soc/sdmmc_periph.h components/soc/include/soc/sens_periph.h -components/soc/include/soc/uart_periph.h components/soc/include/soc/uhci_periph.h components/soc/lldesc.c components/soc/soc_include_legacy_warn.c