Skip to content

Commit

Permalink
Merge branch 'feature/support_uart_sleep_retention' into 'master'
Browse files Browse the repository at this point in the history
feat(uart): support uart module sleep retention

Closes IDF-8457

See merge request espressif/esp-idf!26729
  • Loading branch information
songruo committed Jun 3, 2024
2 parents 8d3f4ca + dca7c28 commit 2d8c719
Show file tree
Hide file tree
Showing 66 changed files with 707 additions and 277 deletions.
7 changes: 7 additions & 0 deletions components/esp_driver_uart/include/driver/uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ typedef struct {
lp_uart_sclk_t lp_source_clk; /*!< LP_UART source clock selection */
#endif
};
struct {
#if SOC_UART_SUPPORT_SLEEP_RETENTION
uint32_t backup_before_sleep: 1; /*!< If set, the driver will backup/restore the HP UART registers before entering/after exiting sleep mode.
By this approach, the system can power off HP UART's power domain.
This can save power, but at the expense of more RAM being consumed */
#endif
} flags; /*!< Configuration flags */
} uart_config_t;

/**
Expand Down
114 changes: 106 additions & 8 deletions components/esp_driver_uart/src/uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
#include <string.h>
#include <sys/param.h>
#include <sys/lock.h>
#include "esp_types.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
Expand Down Expand Up @@ -32,6 +33,7 @@
#include "esp_rom_gpio.h"
#include "clk_ctrl_os.h"
#include "esp_pm.h"
#include "esp_private/sleep_retention.h"

#ifdef CONFIG_UART_ISR_IN_IRAM
#define UART_ISR_ATTR IRAM_ATTR
Expand Down Expand Up @@ -95,10 +97,11 @@ static const char *UART_TAG = "uart";
// Check actual UART mode set
#define UART_IS_MODE_SET(uart_number, mode) ((p_uart_obj[uart_number]->uart_mode == mode))

#define UART_CONTEXT_INIT_DEF(uart_num) {\
.hal.dev = UART_LL_GET_HW(uart_num),\
INIT_CRIT_SECTION_LOCK_IN_STRUCT(spinlock)\
.hw_enabled = false,\
#define UART_CONTEXT_INIT_DEF(uart_num) { \
.port_id = uart_num, \
.hal.dev = UART_LL_GET_HW(uart_num), \
INIT_CRIT_SECTION_LOCK_IN_STRUCT(spinlock) \
.hw_enabled = false, \
}

typedef struct {
Expand Down Expand Up @@ -155,9 +158,15 @@ typedef struct {
} uart_obj_t;

typedef struct {
_lock_t mutex; /*!< Protect uart_module_enable, uart_module_disable, retention, etc. */
uart_port_t port_id;
uart_hal_context_t hal; /*!< UART hal context*/
DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(spinlock)
bool hw_enabled;
#if SOC_UART_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
bool retention_link_inited; /*!< Mark whether the retention link is inited */
bool retention_link_created; /*!< Mark whether the retention link is created */
#endif
} uart_context_t;

static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0};
Expand All @@ -181,9 +190,13 @@ static uart_context_t uart_context[UART_NUM_MAX] = {

static portMUX_TYPE uart_selectlock = portMUX_INITIALIZER_UNLOCKED;

#if SOC_UART_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
static esp_err_t uart_create_sleep_retention_link_cb(void *arg);
#endif

static void uart_module_enable(uart_port_t uart_num)
{
UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock));
_lock_acquire(&(uart_context[uart_num].mutex));
if (uart_context[uart_num].hw_enabled != true) {
if (uart_num < SOC_UART_HP_NUM) {
HP_UART_BUS_CLK_ATOMIC() {
Expand All @@ -194,6 +207,28 @@ static void uart_module_enable(uart_port_t uart_num)
uart_ll_reset_register(uart_num);
}
}

#if SOC_UART_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
// Initialize sleep retention module for HP UART
if (uart_num != CONFIG_ESP_CONSOLE_UART_NUM) { // Console uart retention has been taken care in sleep_sys_periph_stdout_console_uart_retention_init
assert(!uart_context[uart_num].retention_link_inited);
sleep_retention_module_t module = UART_LL_SLEEP_RETENTION_MODULE_ID(uart_num);
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = {
.handle = uart_create_sleep_retention_link_cb,
.arg = &uart_context[uart_num],
},
},
.depends = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM),
};
if (sleep_retention_module_init(module, &init_param) == ESP_OK) {
uart_context[uart_num].retention_link_inited = true;
} else {
ESP_LOGW(UART_TAG, "init sleep retention failed for uart%d, power domain may be turned off during sleep", uart_num);
}
}
#endif
}
#if (SOC_UART_LP_NUM >= 1)
else {
Expand All @@ -205,14 +240,24 @@ static void uart_module_enable(uart_port_t uart_num)
#endif
uart_context[uart_num].hw_enabled = true;
}
UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock));
_lock_release(&(uart_context[uart_num].mutex));
}

static void uart_module_disable(uart_port_t uart_num)
{
UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock));
_lock_acquire(&(uart_context[uart_num].mutex));
if (uart_context[uart_num].hw_enabled != false) {
if (uart_num != CONFIG_ESP_CONSOLE_UART_NUM && uart_num < SOC_UART_HP_NUM) {
#if SOC_UART_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
// Uninitialize sleep retention module for HP UART
sleep_retention_module_t module = UART_LL_SLEEP_RETENTION_MODULE_ID(uart_num);
assert(!uart_context[uart_num].retention_link_created); // HP UART sleep retention should have been freed at this moment
if (uart_context[uart_num].retention_link_inited) {
sleep_retention_module_deinit(module);
uart_context[uart_num].retention_link_inited = false;
}
#endif

HP_UART_BUS_CLK_ATOMIC() {
uart_ll_enable_bus_clock(uart_num, false);
}
Expand All @@ -226,7 +271,7 @@ static void uart_module_disable(uart_port_t uart_num)
#endif
uart_context[uart_num].hw_enabled = false;
}
UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock));
_lock_release(&(uart_context[uart_num].mutex));
}

esp_err_t uart_get_sclk_freq(uart_sclk_t sclk, uint32_t *out_freq_hz)
Expand Down Expand Up @@ -799,6 +844,31 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf

uart_module_enable(uart_num);

#if SOC_UART_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
// Create sleep retention link if desired
if (uart_num != CONFIG_ESP_CONSOLE_UART_NUM && uart_num < SOC_UART_HP_NUM) {
_lock_acquire(&(uart_context[uart_num].mutex));
sleep_retention_module_t module = UART_LL_SLEEP_RETENTION_MODULE_ID(uart_num);
if (uart_config->flags.backup_before_sleep && !uart_context[uart_num].retention_link_created) {
if (uart_context[uart_num].retention_link_inited) {
if (sleep_retention_module_allocate(module) == ESP_OK) {
uart_context[uart_num].retention_link_created = true;
} else {
// Even though the sleep retention module create failed, UART driver should still work, so just warning here
ESP_LOGW(UART_TAG, "create retention module failed, power domain can't turn off");
}
} else {
ESP_LOGW(UART_TAG, "retention module not initialized first, unable to create retention module");
}
} else if (!uart_config->flags.backup_before_sleep && uart_context[uart_num].retention_link_created) {
assert(uart_context[uart_num].retention_link_inited);
sleep_retention_module_free(module);
uart_context[uart_num].retention_link_created = false;
}
_lock_release(&(uart_context[uart_num].mutex));
}
#endif

soc_module_clk_t uart_sclk_sel = 0; // initialize to an invalid module clock ID
if (uart_num < SOC_UART_HP_NUM) {
uart_sclk_sel = (soc_module_clk_t)((uart_config->source_clk) ? uart_config->source_clk : UART_SCLK_DEFAULT); // if no specifying the clock source (soc_module_clk_t starts from 1), then just use the default clock
Expand Down Expand Up @@ -1712,6 +1782,20 @@ esp_err_t uart_driver_delete(uart_port_t uart_num)
periph_rtc_dig_clk8m_disable();
}
#endif

#if SOC_UART_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
// Free sleep retention link for HP UART
if (uart_num != CONFIG_ESP_CONSOLE_UART_NUM && uart_num < SOC_UART_HP_NUM) {
sleep_retention_module_t module = UART_LL_SLEEP_RETENTION_MODULE_ID(uart_num);
_lock_acquire(&(uart_context[uart_num].mutex));
if (uart_context[uart_num].retention_link_created) {
assert(uart_context[uart_num].retention_link_inited);
sleep_retention_module_free(module);
uart_context[uart_num].retention_link_created = false;
}
_lock_release(&(uart_context[uart_num].mutex));
}
#endif
uart_module_disable(uart_num);
return ESP_OK;
}
Expand Down Expand Up @@ -1867,3 +1951,17 @@ void uart_set_always_rx_timeout(uart_port_t uart_num, bool always_rx_timeout)
p_uart_obj[uart_num]->rx_always_timeout_flg = false;
}
}

#if SOC_UART_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
static esp_err_t uart_create_sleep_retention_link_cb(void *arg)
{
uart_context_t *group = (uart_context_t *)arg;
uart_port_t uart_num = group->port_id;
sleep_retention_module_t module = UART_LL_SLEEP_RETENTION_MODULE_ID(uart_num);
esp_err_t err = sleep_retention_entries_create(uart_reg_retention_info[uart_num].regdma_entry_array,
uart_reg_retention_info[uart_num].array_size,
REGDMA_LINK_PRI_UART, module);
ESP_RETURN_ON_ERROR(err, UART_TAG, "create retention link failed");
return ESP_OK;
}
#endif
14 changes: 13 additions & 1 deletion components/esp_driver_uart/test_apps/uart/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
set(srcs "test_app_main.c"
"test_uart.c")

if(CONFIG_PM_ENABLE)
list(APPEND srcs "test_uart_auto_lightsleep.c")
endif()

# Only if the target supports uart retention and the sdkconfig.ci.xxx contains at least PM_ENABLE=y
if(CONFIG_SOC_UART_SUPPORT_SLEEP_RETENTION AND CONFIG_PM_ENABLE)
list(APPEND srcs "test_uart_retention.c")
endif()

# 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" "test_uart_auto_lightsleep.c"
SRCS ${srcs}
REQUIRES esp_driver_uart unity esp_psram test_utils esp_driver_gpio esp_pm
PRIV_INCLUDE_DIRS .
WHOLE_ARCHIVE
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -27,15 +27,14 @@

#if CONFIG_XTAL_FREQ_40
#define MIN_FREQ 10
#elif CONFIG_XTAL_FREQ_48
#define MIN_FREQ 12
#elif CONFIG_XTAL_FREQ_32
#define MIN_FREQ 8
#elif CONFIG_XTAL_FREQ_26
#define MIN_FREQ 13
#endif

#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 = {};
Expand Down Expand Up @@ -82,10 +81,8 @@ TEST_CASE("uart tx won't be blocked by auto light sleep", "[uart]")
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);
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
pm_config.light_sleep_enable = false;
TEST_ESP_OK(esp_pm_configure(&pm_config));
#endif
}
#endif // CONFIG_PM_ENABLE
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32P4)
Loading

0 comments on commit 2d8c719

Please sign in to comment.