From 526a3e49edef812071d9a75ef47e613f05a36407 Mon Sep 17 00:00:00 2001 From: Xia Xiaotian Date: Thu, 7 May 2020 16:15:56 +0800 Subject: [PATCH] esp_wifi: When WiFi TSF is active, skip light sleep * Add an API for peripherals to set callbacks to skip light sleep * Make WiFi power save example work --- components/esp32s2/clk.c | 5 +++ components/esp32s2/include/esp_sleep.h | 9 +++- components/esp32s2/pm_esp32s2.c | 43 ++++++++++++++++++- components/esp32s2/sleep_modes.c | 8 ++++ .../esp_common/include/esp_private/pm_impl.h | 32 ++++++++++++++ components/esp_wifi/esp32s2/esp_adapter.c | 10 ++++- .../esp_wifi/include/esp_private/wifi.h | 11 +++++ components/esp_wifi/src/wifi_init.c | 17 ++++++++ components/soc/soc/esp32s2/include/soc/rtc.h | 2 +- examples/wifi/power_save/README.md | 3 -- .../wifi/power_save/main/Kconfig.projbuild | 6 +-- examples/wifi/power_save/main/power_save.c | 4 ++ 12 files changed, 140 insertions(+), 10 deletions(-) diff --git a/components/esp32s2/clk.c b/components/esp32s2/clk.c index 4d2eb5c46..36ed52dd3 100644 --- a/components/esp32s2/clk.c +++ b/components/esp32s2/clk.c @@ -320,6 +320,11 @@ void esp_perip_clk_init(void) /* Enable WiFi MAC and POWER clocks */ DPORT_SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_WIFI_EN); + /* Set WiFi light sleep clock source to RTC slow clock */ + DPORT_REG_SET_FIELD(DPORT_BT_LPCK_DIV_INT_REG, DPORT_BT_LPCK_DIV_NUM, 0); + DPORT_CLEAR_PERI_REG_MASK(DPORT_BT_LPCK_DIV_FRAC_REG, DPORT_LPCLK_SEL_8M); + DPORT_SET_PERI_REG_MASK(DPORT_BT_LPCK_DIV_FRAC_REG, DPORT_LPCLK_SEL_RTC_SLOW); + /* Enable RNG clock. */ periph_module_enable(PERIPH_RNG_MODULE); } diff --git a/components/esp32s2/include/esp_sleep.h b/components/esp32s2/include/esp_sleep.h index 595824365..58d3f6420 100644 --- a/components/esp32s2/include/esp_sleep.h +++ b/components/esp32s2/include/esp_sleep.h @@ -64,6 +64,7 @@ typedef enum { ESP_SLEEP_WAKEUP_ULP, //!< Wakeup caused by ULP program ESP_SLEEP_WAKEUP_GPIO, //!< Wakeup caused by GPIO (light sleep only) ESP_SLEEP_WAKEUP_UART, //!< Wakeup caused by UART (light sleep only) + ESP_SLEEP_WAKEUP_WIFI, //!< Wakeup caused by WIFI (light sleep only) } esp_sleep_source_t; /* Leave this type define for compatibility */ @@ -239,6 +240,13 @@ esp_err_t esp_sleep_enable_uart_wakeup(int uart_num); */ uint64_t esp_sleep_get_ext1_wakeup_status(void); +/** + * @brief Enable wakeup by WiFi MAC + * @return + * - ESP_OK on success + */ +esp_err_t esp_sleep_enable_wifi_wakeup(void); + /** * @brief Set power down mode for an RTC power domain in sleep mode * @@ -349,7 +357,6 @@ esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void); */ void esp_default_wake_deep_sleep(void); - #ifdef __cplusplus } #endif diff --git a/components/esp32s2/pm_esp32s2.c b/components/esp32s2/pm_esp32s2.c index 2bfd4c351..99eb75e54 100644 --- a/components/esp32s2/pm_esp32s2.c +++ b/components/esp32s2/pm_esp32s2.c @@ -80,6 +80,11 @@ static uint32_t s_ccount_div; static uint32_t s_ccount_mul; #if CONFIG_FREERTOS_USE_TICKLESS_IDLE +#define PERIPH_SKIP_LIGHT_SLEEP_NO 1 + +/* Indicates if light sleep shoule be skipped by peripherals. */ +static skip_light_sleep_cb_t s_periph_skip_light_sleep_cb[PERIPH_SKIP_LIGHT_SLEEP_NO]; + /* Indicates if light sleep entry was skipped in vApplicationSleep for given CPU. * This in turn gets used in IDLE hook to decide if `waiti` needs * to be invoked or not. @@ -477,6 +482,42 @@ void esp_pm_impl_waiti(void) #if CONFIG_FREERTOS_USE_TICKLESS_IDLE +esp_err_t esp_pm_register_skip_light_sleep_callback(skip_light_sleep_cb_t cb) +{ + for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) { + if (s_periph_skip_light_sleep_cb[i] == cb) { + return ESP_OK; + } else if (s_periph_skip_light_sleep_cb[i] == NULL) { + s_periph_skip_light_sleep_cb[i] = cb; + return ESP_OK; + } + } + return ESP_ERR_NO_MEM; +} + +esp_err_t esp_pm_unregister_skip_light_sleep_callback(skip_light_sleep_cb_t cb) +{ + for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) { + if (s_periph_skip_light_sleep_cb[i] == cb) { + s_periph_skip_light_sleep_cb[i] = NULL; + return ESP_OK; + } + } + return ESP_ERR_INVALID_STATE; +} + +static inline bool IRAM_ATTR periph_should_skip_light_sleep(void) +{ + for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) { + if (s_periph_skip_light_sleep_cb[i]) { + if (s_periph_skip_light_sleep_cb[i]() == true) { + return true; + } + } + } + return false; +} + static inline bool IRAM_ATTR should_skip_light_sleep(int core_id) { #if portNUM_PROCESSORS == 2 @@ -486,7 +527,7 @@ static inline bool IRAM_ATTR should_skip_light_sleep(int core_id) return true; } #endif // portNUM_PROCESSORS == 2 - if (s_mode != PM_MODE_LIGHT_SLEEP || s_is_switching) { + if (s_mode != PM_MODE_LIGHT_SLEEP || s_is_switching || periph_should_skip_light_sleep()) { s_skipped_light_sleep[core_id] = true; } else { s_skipped_light_sleep[core_id] = false; diff --git a/components/esp32s2/sleep_modes.c b/components/esp32s2/sleep_modes.c index 5acee19af..2fc3541a6 100644 --- a/components/esp32s2/sleep_modes.c +++ b/components/esp32s2/sleep_modes.c @@ -582,6 +582,12 @@ esp_err_t esp_sleep_enable_uart_wakeup(int uart_num) return ESP_OK; } +esp_err_t esp_sleep_enable_wifi_wakeup(void) +{ + s_config.wakeup_triggers |= RTC_WIFI_TRIG_EN; + return ESP_OK; +} + esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void) { if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET && !s_light_sleep_wakeup) { @@ -603,6 +609,8 @@ esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void) return ESP_SLEEP_WAKEUP_GPIO; } else if (wakeup_cause & (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN)) { return ESP_SLEEP_WAKEUP_UART; + } else if (wakeup_cause & RTC_WIFI_TRIG_EN) { + return ESP_SLEEP_WAKEUP_WIFI; } else { return ESP_SLEEP_WAKEUP_UNDEFINED; } diff --git a/components/esp_common/include/esp_private/pm_impl.h b/components/esp_common/include/esp_private/pm_impl.h index f0cf914f7..71d41bd7c 100644 --- a/components/esp_common/include/esp_private/pm_impl.h +++ b/components/esp_common/include/esp_private/pm_impl.h @@ -109,6 +109,38 @@ void esp_pm_impl_dump_stats(FILE* out); */ void esp_pm_impl_waiti(void); +#if CONFIG_IDF_TARGET_ESP32S2 +/** + * @brief Callback function type for peripherals to skip light sleep. + * + */ +typedef bool (* skip_light_sleep_cb_t)(void); + +/** + * @brief Register peripherals skip light sleep callback + * + * This function allows you to register a callback that gets the result + * that if light sleep should be skipped by peripherals. + * @param cb function to get the result + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM if no more callback slots are available + */ +esp_err_t esp_pm_register_skip_light_sleep_callback(skip_light_sleep_cb_t cb); + +/** + * @brief Unregisterperipherals skip light sleep callback + * + * This function allows you to unregister a callback which was previously + * registered using esp_register_skip_light_sleep_callback. + * @param cb function to get the result + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if the given callback hasn't been registered before + */ +esp_err_t esp_pm_unregister_skip_light_sleep_callback(skip_light_sleep_cb_t cb); +#endif + #ifdef CONFIG_PM_PROFILING #define WITH_PROFILING #endif diff --git a/components/esp_wifi/esp32s2/esp_adapter.c b/components/esp_wifi/esp32s2/esp_adapter.c index a7b2e7438..3445fce61 100644 --- a/components/esp_wifi/esp32s2/esp_adapter.c +++ b/components/esp_wifi/esp32s2/esp_adapter.c @@ -422,6 +422,14 @@ static int get_time_wrapper(void *t) return os_get_time(t); } +static uint32_t esp_clk_slowclk_cal_get_wrapper(void) +{ + /* The bit width of WiFi light sleep clock calibration is 12 while the one of + * system is 19. It should shift 19 - 12 = 7. + */ + return (esp_clk_slowclk_cal_get() >> 7); +} + static void * IRAM_ATTR malloc_internal_wrapper(size_t size) { return heap_caps_malloc(size, MALLOC_CAP_8BIT|MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL); @@ -604,7 +612,7 @@ wifi_osi_funcs_t g_wifi_osi_funcs = { ._get_time = get_time_wrapper, ._random = os_random, #if CONFIG_IDF_TARGET_ESP32S2 - ._slowclk_cal_get = esp_clk_slowclk_cal_get, + ._slowclk_cal_get = esp_clk_slowclk_cal_get_wrapper, #endif ._log_write = esp_log_write, ._log_writev = esp_log_writev, diff --git a/components/esp_wifi/include/esp_private/wifi.h b/components/esp_wifi/include/esp_private/wifi.h index 4e995b679..8d21c64e8 100644 --- a/components/esp_wifi/include/esp_private/wifi.h +++ b/components/esp_wifi/include/esp_private/wifi.h @@ -414,6 +414,17 @@ esp_err_t esp_wifi_internal_get_negotiated_channel(wifi_interface_t ifx, uint8_t */ esp_err_t esp_wifi_internal_get_negotiated_bandwidth(wifi_interface_t ifx, uint8_t aid, uint8_t *bw); +#if CONFIG_IDF_TARGET_ESP32S2 +/** + * @brief Check if WiFi TSF is active + * + * @return + * - true: Active + * - false: Not active + */ +bool esp_wifi_internal_is_tsf_active(void); +#endif + #ifdef __cplusplus } #endif diff --git a/components/esp_wifi/src/wifi_init.c b/components/esp_wifi/src/wifi_init.c index 5946758b7..4cf1d8c62 100644 --- a/components/esp_wifi/src/wifi_init.c +++ b/components/esp_wifi/src/wifi_init.c @@ -17,6 +17,8 @@ #include "esp_log.h" #include "esp_private/wifi.h" #include "esp_pm.h" +#include "esp_sleep.h" +#include "esp_private/pm_impl.h" #include "soc/rtc.h" #include "esp_wpa.h" #include "esp_netif.h" @@ -121,6 +123,11 @@ esp_err_t esp_wifi_deinit(void) #if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER tcpip_adapter_clear_default_wifi_handlers(); +#endif +#if CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE + esp_pm_unregister_skip_light_sleep_callback(esp_wifi_internal_is_tsf_active); +#endif #endif return err; @@ -137,6 +144,16 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config) } } #endif +#if CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE + esp_err_t ret = esp_pm_register_skip_light_sleep_callback(esp_wifi_internal_is_tsf_active); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to register skip light sleep callback (0x%x)", ret); + return ret; + } + esp_sleep_enable_wifi_wakeup(); +#endif +#endif #if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER esp_err_t err = tcpip_adapter_set_default_wifi_handlers(); if (err != ESP_OK) { diff --git a/components/soc/soc/esp32s2/include/soc/rtc.h b/components/soc/soc/esp32s2/include/soc/rtc.h index 5f30be6d2..2f7ba5c4e 100644 --- a/components/soc/soc/esp32s2/include/soc/rtc.h +++ b/components/soc/soc/esp32s2/include/soc/rtc.h @@ -700,7 +700,7 @@ void rtc_sleep_set_wakeup_time(uint64_t t); #define RTC_GPIO_TRIG_EN BIT(2) //!< GPIO wakeup (light sleep only) #define RTC_TIMER_TRIG_EN BIT(3) //!< Timer wakeup #define RTC_SDIO_TRIG_EN BIT(4) //!< SDIO wakeup (light sleep only) -#define RTC_MAC_TRIG_EN BIT(5) //!< MAC wakeup (light sleep only) +#define RTC_WIFI_TRIG_EN BIT(5) //!< WIFI wakeup (light sleep only) #define RTC_UART0_TRIG_EN BIT(6) //!< UART0 wakeup (light sleep only) #define RTC_UART1_TRIG_EN BIT(7) //!< UART1 wakeup (light sleep only) #define RTC_TOUCH_TRIG_EN BIT(8) //!< Touch wakeup diff --git a/examples/wifi/power_save/README.md b/examples/wifi/power_save/README.md index f719f00b9..6d3457014 100644 --- a/examples/wifi/power_save/README.md +++ b/examples/wifi/power_save/README.md @@ -1,6 +1,3 @@ -| Supported Targets | ESP32 | -| ----------------- | ----- | - # Wifi Power Save Example This example shows how to use power save mode of wifi. diff --git a/examples/wifi/power_save/main/Kconfig.projbuild b/examples/wifi/power_save/main/Kconfig.projbuild index 27ee75660..0d733fbad 100644 --- a/examples/wifi/power_save/main/Kconfig.projbuild +++ b/examples/wifi/power_save/main/Kconfig.projbuild @@ -70,13 +70,13 @@ menu "Example Configuration" config EXAMPLE_MIN_CPU_FREQ_40M bool "40 MHz (use with 40MHz XTAL)" - depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO + depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO config EXAMPLE_MIN_CPU_FREQ_20M bool "20 MHz (use with 40MHz XTAL)" - depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO + depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO config EXAMPLE_MIN_CPU_FREQ_10M bool "10 MHz (use with 40MHz XTAL)" - depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO + depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO config EXAMPLE_MIN_CPU_FREQ_26M bool "26 MHz (use with 26MHz XTAL)" depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO diff --git a/examples/wifi/power_save/main/power_save.c b/examples/wifi/power_save/main/power_save.c index bf8456de4..6e6203b9d 100644 --- a/examples/wifi/power_save/main/power_save.c +++ b/examples/wifi/power_save/main/power_save.c @@ -95,7 +95,11 @@ void app_main(void) // Configure dynamic frequency scaling: // maximum and minimum frequencies are set in sdkconfig, // automatic light sleep is enabled if tickless idle support is enabled. +#if CONFIG_IDF_TARGET_ESP32 esp_pm_config_esp32_t pm_config = { +#elif CONFIG_IDF_TARGET_ESP32S2 + esp_pm_config_esp32s2_t pm_config = { +#endif .max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ, .min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ, #if CONFIG_FREERTOS_USE_TICKLESS_IDLE