Эх сурвалжийг харах

Merge branch 'bugfix/esp32s2_wifi_skip_light_sleep' into 'master'

esp_wifi: When WiFi TSF is active, skip light sleep

Closes WIFI-2305 and WIFI-2306

See merge request espressif/esp-idf!8639
Jiang Jiang Jian 5 жил өмнө
parent
commit
26ab1c54ec

+ 5 - 0
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);
 }

+ 8 - 1
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

+ 42 - 1
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;

+ 8 - 0
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;
     }

+ 32 - 0
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

+ 9 - 1
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,

+ 11 - 0
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

+ 17 - 0
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) {

+ 1 - 1
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

+ 0 - 3
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.

+ 3 - 3
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

+ 4 - 0
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