mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
bugfix: fix sleep failed caused by uart clk lost
This commit is contained in:
parent
2ab094192d
commit
713da85988
@ -127,6 +127,8 @@
|
||||
// Actually costs 80us, using the fastest slow clock 150K calculation takes about 16 ticks
|
||||
#define SLEEP_TIMER_ALARM_TO_SLEEP_TICKS (16)
|
||||
|
||||
#define SLEEP_UART_FLUSH_DONE_TO_SLEEP_US (450)
|
||||
|
||||
#if SOC_PM_SUPPORT_TOP_PD
|
||||
// IDF console uses 8 bits data mode without parity, so each char occupy 8(data)+1(start)+1(stop)=10bits
|
||||
#define UART_FLUSH_US_PER_CHAR (10*1000*1000 / CONFIG_ESP_CONSOLE_UART_BAUDRATE)
|
||||
@ -235,7 +237,7 @@ static void ext0_wakeup_prepare(void);
|
||||
#if SOC_PM_SUPPORT_EXT1_WAKEUP
|
||||
static void ext1_wakeup_prepare(void);
|
||||
#endif
|
||||
static esp_err_t timer_wakeup_prepare(void);
|
||||
static esp_err_t timer_wakeup_prepare(int64_t sleep_duration);
|
||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
static void touch_wakeup_prepare(void);
|
||||
#endif
|
||||
@ -395,12 +397,14 @@ static void IRAM_ATTR flush_uarts(void)
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t s_suspended_uarts_bmap = 0;
|
||||
|
||||
/**
|
||||
* Suspend enabled uarts and return suspended uarts bit map
|
||||
*/
|
||||
static uint32_t IRAM_ATTR suspend_uarts(void)
|
||||
static IRAM_ATTR void suspend_uarts(void)
|
||||
{
|
||||
uint32_t suspended_uarts_bmap = 0;
|
||||
s_suspended_uarts_bmap = 0;
|
||||
for (int i = 0; i < SOC_UART_NUM; ++i) {
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
if (!periph_ll_periph_enabled(PERIPH_UART0_MODULE + i)) {
|
||||
@ -408,7 +412,7 @@ static uint32_t IRAM_ATTR suspend_uarts(void)
|
||||
}
|
||||
#endif
|
||||
uart_ll_force_xoff(i);
|
||||
suspended_uarts_bmap |= BIT(i);
|
||||
s_suspended_uarts_bmap |= BIT(i);
|
||||
#if SOC_UART_SUPPORT_FSM_TX_WAIT_SEND
|
||||
uint32_t uart_fsm = 0;
|
||||
do {
|
||||
@ -418,19 +422,57 @@ static uint32_t IRAM_ATTR suspend_uarts(void)
|
||||
while (uart_ll_get_fsm_status(i) != 0) {}
|
||||
#endif
|
||||
}
|
||||
return suspended_uarts_bmap;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR resume_uarts(uint32_t uarts_resume_bmap)
|
||||
static void IRAM_ATTR resume_uarts(void)
|
||||
{
|
||||
for (int i = 0; i < SOC_UART_NUM; ++i) {
|
||||
if (uarts_resume_bmap & 0x1) {
|
||||
if (s_suspended_uarts_bmap & 0x1) {
|
||||
uart_ll_force_xon(i);
|
||||
}
|
||||
uarts_resume_bmap >>= 1;
|
||||
s_suspended_uarts_bmap >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
UART prepare strategy in sleep:
|
||||
Deepsleep : flush the fifo before enter sleep to avoid data loss
|
||||
|
||||
Lightsleep:
|
||||
Chips not support PD_TOP: Suspend uart before cpu freq switch
|
||||
|
||||
Chips support PD_TOP:
|
||||
For sleep which will not power down the TOP domain (uart belongs it), we can just suspend the UART.
|
||||
|
||||
For sleep which will power down the TOP domain, we need to consider whether the uart flushing will
|
||||
block the sleep process and cause the rtos target tick to be missed upon waking up. It's need to
|
||||
estimate the flush time based on the number of bytes in the uart FIFO, if the predicted flush
|
||||
completion time has exceeded the wakeup time, we should abandon the flush, skip the sleep and
|
||||
return ESP_ERR_SLEEP_REJECT.
|
||||
*/
|
||||
static bool light_sleep_uart_prepare(uint32_t pd_flags, int64_t sleep_duration)
|
||||
{
|
||||
bool should_skip_sleep = false;
|
||||
#if !SOC_PM_SUPPORT_TOP_PD
|
||||
suspend_uarts();
|
||||
#else
|
||||
if (pd_flags & PMU_SLEEP_PD_TOP) {
|
||||
if ((s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) &&
|
||||
// +1 is for cover the last charactor flush time
|
||||
(sleep_duration < (int64_t)((UART_LL_FIFO_DEF_LEN - uart_ll_get_txfifo_len(CONSOLE_UART_DEV) + 1) * UART_FLUSH_US_PER_CHAR) + SLEEP_UART_FLUSH_DONE_TO_SLEEP_US)) {
|
||||
should_skip_sleep = true;
|
||||
} else {
|
||||
/* Only flush the uart_num configured to console, the transmission integrity of
|
||||
other uarts is guaranteed by the UART driver */
|
||||
esp_rom_uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
|
||||
}
|
||||
} else {
|
||||
suspend_uarts();
|
||||
}
|
||||
#endif
|
||||
return should_skip_sleep;
|
||||
}
|
||||
|
||||
/**
|
||||
* These save-restore workaround should be moved to lower layer
|
||||
*/
|
||||
@ -492,8 +534,8 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t mo
|
||||
// For deep sleep, wait for the contents of UART FIFO to be sent.
|
||||
bool deep_sleep = (mode == ESP_SLEEP_MODE_DEEP_SLEEP);
|
||||
bool should_skip_sleep = false;
|
||||
uint32_t suspended_uarts_bmap = 0;
|
||||
|
||||
int64_t sleep_duration = (int64_t) s_config.sleep_duration - (int64_t) s_config.sleep_time_adjustment;
|
||||
|
||||
#if SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256
|
||||
//Keep the RTC8M_CLK on if RTC clock is rc_fast_d256.
|
||||
@ -512,23 +554,18 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t mo
|
||||
//turn down MSPI speed
|
||||
mspi_timing_change_speed_mode_cache_safe(true);
|
||||
|
||||
// Sleep UART prepare
|
||||
if (deep_sleep) {
|
||||
flush_uarts();
|
||||
} else {
|
||||
should_skip_sleep = light_sleep_uart_prepare(pd_flags, sleep_duration);
|
||||
}
|
||||
|
||||
// Save current frequency and switch to XTAL
|
||||
rtc_cpu_freq_config_t cpu_freq_config;
|
||||
rtc_clk_cpu_freq_get_config(&cpu_freq_config);
|
||||
rtc_clk_cpu_freq_set_xtal();
|
||||
|
||||
// Deep sleep UART prepare
|
||||
/* flush_uart should be as late as possible, because the later the flush,
|
||||
the shorter the time overhead of entering sleep caused by blocking,
|
||||
and blocking after frequency switching can also reduce the power consumption
|
||||
during the active state.*/
|
||||
|
||||
/* ext/gpio deepsleep wakeup prepare will change GPIO configure,
|
||||
we need to flush uart before it */
|
||||
if (deep_sleep) {
|
||||
flush_uarts();
|
||||
}
|
||||
|
||||
#if SOC_PM_SUPPORT_EXT0_WAKEUP
|
||||
// Configure pins for external wakeup
|
||||
if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) {
|
||||
@ -620,42 +657,21 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t mo
|
||||
#endif
|
||||
|
||||
// Configure timer wakeup
|
||||
if (s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) {
|
||||
if (timer_wakeup_prepare() != ESP_OK) {
|
||||
result = ESP_ERR_SLEEP_REJECT;
|
||||
if (!should_skip_sleep && (s_config.wakeup_triggers & RTC_TIMER_TRIG_EN)) {
|
||||
if (timer_wakeup_prepare(sleep_duration) != ESP_OK) {
|
||||
should_skip_sleep = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Light sleep UART prepare
|
||||
if (!deep_sleep) {
|
||||
#if SOC_PM_SUPPORT_TOP_PD
|
||||
if (pd_flags & PMU_SLEEP_PD_TOP) {
|
||||
if ((s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) &&
|
||||
// s_config.sleep_duration here has been compensated in timer_wakeup_prepare,
|
||||
// +2 is for cover the last charactor flush time and timer alarm to sleep request time(no more than 80us)
|
||||
(s_config.sleep_duration < (UART_LL_FIFO_DEF_LEN - uart_ll_get_txfifo_len(CONSOLE_UART_DEV) + 2) * UART_FLUSH_US_PER_CHAR)) {
|
||||
result = ESP_ERR_SLEEP_REJECT;
|
||||
should_skip_sleep = true;
|
||||
} else {
|
||||
/* Only flush the uart_num configured to console, the transmission integrity of
|
||||
other uarts is guaranteed by the UART driver */
|
||||
esp_rom_uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
suspended_uarts_bmap = suspend_uarts();
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_SLEEP_SYSTIMER_STALL_WORKAROUND
|
||||
if (!(pd_flags & RTC_SLEEP_PD_XTAL)) {
|
||||
rtc_sleep_systimer_enable(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!should_skip_sleep) {
|
||||
if (should_skip_sleep) {
|
||||
result = ESP_ERR_SLEEP_REJECT;
|
||||
} else {
|
||||
if (deep_sleep) {
|
||||
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
|
||||
esp_sleep_isolate_digital_gpio();
|
||||
@ -736,7 +752,7 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t mo
|
||||
}
|
||||
|
||||
// re-enable UART output
|
||||
resume_uarts(suspended_uarts_bmap);
|
||||
resume_uarts();
|
||||
s_lightsleep_cnt++;
|
||||
return result;
|
||||
}
|
||||
@ -1166,9 +1182,8 @@ esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t timer_wakeup_prepare(void)
|
||||
static esp_err_t timer_wakeup_prepare(int64_t sleep_duration)
|
||||
{
|
||||
int64_t sleep_duration = (int64_t) s_config.sleep_duration - (int64_t) s_config.sleep_time_adjustment;
|
||||
if (sleep_duration < 0) {
|
||||
sleep_duration = 0;
|
||||
}
|
||||
@ -1190,7 +1205,6 @@ static esp_err_t timer_wakeup_prepare(void)
|
||||
rtc_hal_set_wakeup_timer(target_wakeup_tick);
|
||||
#endif
|
||||
|
||||
s_config.sleep_duration = sleep_duration;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user