|
|
@@ -1,4 +1,4 @@
|
|
|
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
|
|
+// Copyright 2015-2017 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.
|
|
|
@@ -14,10 +14,13 @@
|
|
|
|
|
|
#include <stddef.h>
|
|
|
#include <sys/lock.h>
|
|
|
+#include <sys/param.h>
|
|
|
#include "esp_attr.h"
|
|
|
-#include "esp_deep_sleep.h"
|
|
|
+#include "esp_sleep.h"
|
|
|
#include "esp_log.h"
|
|
|
#include "esp_clk.h"
|
|
|
+#include "esp_newlib.h"
|
|
|
+#include "esp_spi_flash.h"
|
|
|
#include "rom/cache.h"
|
|
|
#include "rom/rtc.h"
|
|
|
#include "rom/uart.h"
|
|
|
@@ -25,6 +28,7 @@
|
|
|
#include "soc/rtc.h"
|
|
|
#include "soc/rtc_cntl_reg.h"
|
|
|
#include "soc/rtc_io_reg.h"
|
|
|
+#include "soc/spi_reg.h"
|
|
|
#include "soc/sens_reg.h"
|
|
|
#include "soc/dport_reg.h"
|
|
|
#include "driver/rtc_io.h"
|
|
|
@@ -32,11 +36,17 @@
|
|
|
#include "freertos/task.h"
|
|
|
#include "sdkconfig.h"
|
|
|
|
|
|
+// If light sleep time is less than that, don't power down flash
|
|
|
+#define FLASH_PD_MIN_SLEEP_TIME_US 2000
|
|
|
+
|
|
|
+// Time from VDD_SDIO power up to first flash read in ROM code
|
|
|
+#define VDD_SDIO_POWERUP_TO_FLASH_READ_US 700
|
|
|
+
|
|
|
/**
|
|
|
* Internal structure which holds all requested deep sleep parameters
|
|
|
*/
|
|
|
typedef struct {
|
|
|
- esp_deep_sleep_pd_option_t pd_options[ESP_PD_DOMAIN_MAX];
|
|
|
+ esp_sleep_pd_option_t pd_options[ESP_PD_DOMAIN_MAX];
|
|
|
uint64_t sleep_duration;
|
|
|
uint32_t wakeup_triggers : 11;
|
|
|
uint32_t ext1_trigger_mode : 1;
|
|
|
@@ -54,7 +64,7 @@ static deep_sleep_config_t s_config = {
|
|
|
is not thread-safe. */
|
|
|
static _lock_t lock_rtc_memory_crc;
|
|
|
|
|
|
-static const char* TAG = "deepsleep";
|
|
|
+static const char* TAG = "sleep";
|
|
|
|
|
|
static uint32_t get_power_down_flags();
|
|
|
static void ext0_wakeup_prepare();
|
|
|
@@ -106,31 +116,17 @@ void __attribute__((weak, alias("esp_default_wake_deep_sleep"))) esp_wake_deep_s
|
|
|
|
|
|
void esp_deep_sleep(uint64_t time_in_us)
|
|
|
{
|
|
|
- esp_deep_sleep_enable_timer_wakeup(time_in_us);
|
|
|
+ esp_sleep_enable_timer_wakeup(time_in_us);
|
|
|
esp_deep_sleep_start();
|
|
|
}
|
|
|
|
|
|
-void IRAM_ATTR esp_deep_sleep_start()
|
|
|
+static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
|
|
|
{
|
|
|
- // Decide which power domains can be powered down
|
|
|
- uint32_t pd_flags = get_power_down_flags();
|
|
|
-
|
|
|
- // Shut down parts of RTC which may have been left enabled by the wireless drivers
|
|
|
- CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG,
|
|
|
- RTC_CNTL_CKGEN_I2C_PU | RTC_CNTL_PLL_I2C_PU |
|
|
|
- RTC_CNTL_RFRX_PBUS_PU | RTC_CNTL_TXRF_I2C_PU);
|
|
|
-
|
|
|
- SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_M, 0, SENS_FORCE_XPD_SAR_S);
|
|
|
-
|
|
|
// Flush UARTs so that output is not lost due to APB frequency change
|
|
|
uart_tx_wait_idle(0);
|
|
|
uart_tx_wait_idle(1);
|
|
|
uart_tx_wait_idle(2);
|
|
|
|
|
|
- if (esp_get_deep_sleep_wake_stub() == NULL) {
|
|
|
- esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep);
|
|
|
- }
|
|
|
-
|
|
|
// Configure pins for external wakeup
|
|
|
if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) {
|
|
|
ext0_wakeup_prepare();
|
|
|
@@ -148,10 +144,25 @@ void IRAM_ATTR esp_deep_sleep_start()
|
|
|
timer_wakeup_prepare();
|
|
|
}
|
|
|
|
|
|
- // Enter deep sleep
|
|
|
+ // Enter sleep
|
|
|
rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT(pd_flags);
|
|
|
rtc_sleep_init(config);
|
|
|
- rtc_sleep_start(s_config.wakeup_triggers, 0);
|
|
|
+ return rtc_sleep_start(s_config.wakeup_triggers, 0);
|
|
|
+}
|
|
|
+
|
|
|
+void IRAM_ATTR esp_deep_sleep_start()
|
|
|
+{
|
|
|
+ // Configure wake stub
|
|
|
+ if (esp_get_deep_sleep_wake_stub() == NULL) {
|
|
|
+ esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Decide which power domains can be powered down
|
|
|
+ uint32_t pd_flags = get_power_down_flags();
|
|
|
+
|
|
|
+ // Enter sleep
|
|
|
+ esp_sleep_start(RTC_SLEEP_PD_DIG | RTC_SLEEP_PD_VDDSDIO | pd_flags);
|
|
|
+
|
|
|
// Because RTC is in a slower clock domain than the CPU, it
|
|
|
// can take several CPU cycles for the sleep mode to start.
|
|
|
while (1) {
|
|
|
@@ -159,9 +170,101 @@ void IRAM_ATTR esp_deep_sleep_start()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void rtc_wdt_enable(int time_ms)
|
|
|
+{
|
|
|
+ WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE);
|
|
|
+ WRITE_PERI_REG(RTC_CNTL_WDTFEED_REG, 1);
|
|
|
+ REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_SYS_RESET_LENGTH, 7);
|
|
|
+ REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_CPU_RESET_LENGTH, 7);
|
|
|
+ REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_RESET_RTC);
|
|
|
+ WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * time_ms / 1000);
|
|
|
+ SET_PERI_REG_MASK(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN | RTC_CNTL_WDT_PAUSE_IN_SLP);
|
|
|
+ WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0);
|
|
|
+}
|
|
|
+
|
|
|
+static void rtc_wdt_disable()
|
|
|
+{
|
|
|
+ WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE);
|
|
|
+ WRITE_PERI_REG(RTC_CNTL_WDTFEED_REG, 1);
|
|
|
+ REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_OFF);
|
|
|
+ REG_CLR_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN);
|
|
|
+ WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Helper function which handles entry to and exit from light sleep
|
|
|
+ * Placed into IRAM as flash may need some time to be powered on.
|
|
|
+ */
|
|
|
+static esp_err_t IRAM_ATTR esp_light_sleep_inner(uint32_t pd_flags,
|
|
|
+ rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us)
|
|
|
+{
|
|
|
+ // Enter sleep
|
|
|
+ esp_err_t err = esp_sleep_start(pd_flags);
|
|
|
+
|
|
|
+ // Restore CPU frequency
|
|
|
+ rtc_clk_cpu_freq_set(cpu_freq);
|
|
|
+
|
|
|
+ // If SPI flash was powered down, wait for it to become ready
|
|
|
+ if (pd_flags & RTC_SLEEP_PD_VDDSDIO) {
|
|
|
+ // Wait for the flash chip to start up
|
|
|
+ ets_delay_us(flash_enable_time_us);
|
|
|
+ }
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+esp_err_t esp_light_sleep_start()
|
|
|
+{
|
|
|
+ static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED;
|
|
|
+
|
|
|
+ portENTER_CRITICAL(&light_sleep_lock);
|
|
|
+ int other_cpu = xPortGetCoreID() ? 0 : 1;
|
|
|
+ esp_cpu_stall(other_cpu);
|
|
|
+
|
|
|
+ // Other CPU is stalled, need to disable DPORT protection
|
|
|
+ esp_dport_access_int_pause();
|
|
|
+
|
|
|
+ // Decide which power domains can be powered down
|
|
|
+ uint32_t pd_flags = get_power_down_flags();
|
|
|
+
|
|
|
+ // Decide if flash needs to be powered down;
|
|
|
+ // If it needs to be powered down, adjust sleep time
|
|
|
+ const uint32_t flash_enable_time_us = VDD_SDIO_POWERUP_TO_FLASH_READ_US
|
|
|
+ + CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY;
|
|
|
+
|
|
|
+ if (s_config.sleep_duration > FLASH_PD_MIN_SLEEP_TIME_US &&
|
|
|
+ s_config.sleep_duration > flash_enable_time_us) {
|
|
|
+ pd_flags |= RTC_SLEEP_PD_VDDSDIO;
|
|
|
+ s_config.sleep_duration -= flash_enable_time_us;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Safety net: enable WDT in case exit from light sleep fails
|
|
|
+ rtc_wdt_enable(1000);
|
|
|
+
|
|
|
+ // Save current CPU frequency, light sleep will switch to XTAL
|
|
|
+ rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get();
|
|
|
+
|
|
|
+ // Enter sleep, then wait for flash to be ready on wakeup
|
|
|
+ esp_err_t err = esp_light_sleep_inner(pd_flags, cpu_freq, flash_enable_time_us);
|
|
|
+
|
|
|
+ // At this point, if FRC1 is used for timekeeping, time will be lagging behind.
|
|
|
+ // This will update the microsecond count based on RTC timer.
|
|
|
+ esp_set_time_from_rtc();
|
|
|
+
|
|
|
+ // However, we do not advance RTOS ticks here; doing so would be rather messy,
|
|
|
+ // as ticks can only be advanced on CPU0.
|
|
|
+ // If this is needed by the application, automatic light sleep (tickless idle)
|
|
|
+ // will handle that better.
|
|
|
+
|
|
|
+ esp_cpu_unstall(other_cpu);
|
|
|
+ esp_dport_access_int_resume();
|
|
|
+ rtc_wdt_disable();
|
|
|
+ portEXIT_CRITICAL(&light_sleep_lock);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
void system_deep_sleep(uint64_t) __attribute__((alias("esp_deep_sleep")));
|
|
|
|
|
|
-esp_err_t esp_deep_sleep_enable_ulp_wakeup()
|
|
|
+esp_err_t esp_sleep_enable_ulp_wakeup()
|
|
|
{
|
|
|
#ifdef CONFIG_ULP_COPROC_ENABLED
|
|
|
if(s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) {
|
|
|
@@ -175,7 +278,7 @@ esp_err_t esp_deep_sleep_enable_ulp_wakeup()
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
-esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us)
|
|
|
+esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us)
|
|
|
{
|
|
|
s_config.wakeup_triggers |= RTC_TIMER_TRIG_EN;
|
|
|
s_config.sleep_duration = time_in_us;
|
|
|
@@ -190,7 +293,7 @@ static void timer_wakeup_prepare()
|
|
|
rtc_sleep_set_wakeup_time(cur_rtc_count + rtc_count_delta);
|
|
|
}
|
|
|
|
|
|
-esp_err_t esp_deep_sleep_enable_touchpad_wakeup()
|
|
|
+esp_err_t esp_sleep_enable_touchpad_wakeup()
|
|
|
{
|
|
|
if (s_config.wakeup_triggers & (RTC_EXT0_TRIG_EN)) {
|
|
|
ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0");
|
|
|
@@ -200,9 +303,9 @@ esp_err_t esp_deep_sleep_enable_touchpad_wakeup()
|
|
|
return ESP_OK;
|
|
|
}
|
|
|
|
|
|
-touch_pad_t esp_deep_sleep_get_touchpad_wakeup_status()
|
|
|
+touch_pad_t esp_sleep_get_touchpad_wakeup_status()
|
|
|
{
|
|
|
- if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD) {
|
|
|
+ if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TOUCHPAD) {
|
|
|
return TOUCH_PAD_MAX;
|
|
|
}
|
|
|
uint32_t touch_mask = REG_GET_FIELD(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_EN);
|
|
|
@@ -210,7 +313,7 @@ touch_pad_t esp_deep_sleep_get_touchpad_wakeup_status()
|
|
|
return (touch_pad_t) (__builtin_ffs(touch_mask) - 1);
|
|
|
}
|
|
|
|
|
|
-esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level)
|
|
|
+esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level)
|
|
|
{
|
|
|
if (level < 0 || level > 1) {
|
|
|
return ESP_ERR_INVALID_ARG;
|
|
|
@@ -249,7 +352,7 @@ static void ext0_wakeup_prepare()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-esp_err_t esp_deep_sleep_enable_ext1_wakeup(uint64_t mask, esp_ext1_wakeup_mode_t mode)
|
|
|
+esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode)
|
|
|
{
|
|
|
if (mode > ESP_EXT1_WAKEUP_ANY_HIGH) {
|
|
|
return ESP_ERR_INVALID_ARG;
|
|
|
@@ -311,9 +414,9 @@ static void ext1_wakeup_prepare()
|
|
|
s_config.ext1_trigger_mode, RTC_CNTL_EXT_WAKEUP1_LV_S);
|
|
|
}
|
|
|
|
|
|
-uint64_t esp_deep_sleep_get_ext1_wakeup_status()
|
|
|
+uint64_t esp_sleep_get_ext1_wakeup_status()
|
|
|
{
|
|
|
- if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_EXT1) {
|
|
|
+ if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_EXT1) {
|
|
|
return 0;
|
|
|
}
|
|
|
uint32_t status = REG_GET_FIELD(RTC_CNTL_EXT_WAKEUP1_STATUS_REG, RTC_CNTL_EXT_WAKEUP1_STATUS);
|
|
|
@@ -332,30 +435,30 @@ uint64_t esp_deep_sleep_get_ext1_wakeup_status()
|
|
|
return gpio_mask;
|
|
|
}
|
|
|
|
|
|
-esp_deep_sleep_wakeup_cause_t esp_deep_sleep_get_wakeup_cause()
|
|
|
+esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause()
|
|
|
{
|
|
|
if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET) {
|
|
|
- return ESP_DEEP_SLEEP_WAKEUP_UNDEFINED;
|
|
|
+ return ESP_SLEEP_WAKEUP_UNDEFINED;
|
|
|
}
|
|
|
|
|
|
uint32_t wakeup_cause = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_CAUSE);
|
|
|
if (wakeup_cause & RTC_EXT0_TRIG_EN) {
|
|
|
- return ESP_DEEP_SLEEP_WAKEUP_EXT0;
|
|
|
+ return ESP_SLEEP_WAKEUP_EXT0;
|
|
|
} else if (wakeup_cause & RTC_EXT1_TRIG_EN) {
|
|
|
- return ESP_DEEP_SLEEP_WAKEUP_EXT1;
|
|
|
+ return ESP_SLEEP_WAKEUP_EXT1;
|
|
|
} else if (wakeup_cause & RTC_TIMER_TRIG_EN) {
|
|
|
- return ESP_DEEP_SLEEP_WAKEUP_TIMER;
|
|
|
+ return ESP_SLEEP_WAKEUP_TIMER;
|
|
|
} else if (wakeup_cause & RTC_TOUCH_TRIG_EN) {
|
|
|
- return ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD;
|
|
|
+ return ESP_SLEEP_WAKEUP_TOUCHPAD;
|
|
|
} else if (wakeup_cause & RTC_ULP_TRIG_EN) {
|
|
|
- return ESP_DEEP_SLEEP_WAKEUP_ULP;
|
|
|
+ return ESP_SLEEP_WAKEUP_ULP;
|
|
|
} else {
|
|
|
- return ESP_DEEP_SLEEP_WAKEUP_UNDEFINED;
|
|
|
+ return ESP_SLEEP_WAKEUP_UNDEFINED;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-esp_err_t esp_deep_sleep_pd_config(esp_deep_sleep_pd_domain_t domain,
|
|
|
- esp_deep_sleep_pd_option_t option)
|
|
|
+esp_err_t esp_sleep_pd_config(esp_sleep_pd_domain_t domain,
|
|
|
+ esp_sleep_pd_option_t option)
|
|
|
{
|
|
|
if (domain >= ESP_PD_DOMAIN_MAX || option > ESP_PD_OPTION_AUTO) {
|
|
|
return ESP_ERR_INVALID_ARG;
|
|
|
@@ -410,7 +513,7 @@ static uint32_t get_power_down_flags()
|
|
|
option_str[s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM]]);
|
|
|
|
|
|
// Prepare flags based on the selected options
|
|
|
- uint32_t pd_flags = RTC_SLEEP_PD_DIG;
|
|
|
+ uint32_t pd_flags = 0;
|
|
|
if (s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] != ESP_PD_OPTION_ON) {
|
|
|
pd_flags |= RTC_SLEEP_PD_RTC_FAST_MEM;
|
|
|
}
|