]> granicus.if.org Git - esp-idf/commitdiff
freertos,esp32: automatic light sleep support
authorIvan Grokhotkov <ivan@espressif.com>
Thu, 12 Apr 2018 10:18:45 +0000 (18:18 +0800)
committerbot <bot@espressif.com>
Fri, 18 May 2018 03:14:46 +0000 (03:14 +0000)
components/esp32/freertos_hooks.c
components/esp32/pm_esp32.c
components/esp32/pm_trace.c
components/esp32/pm_trace.h
components/esp32/test/test_pm.c
components/freertos/Kconfig
components/freertos/include/freertos/FreeRTOSConfig.h
components/freertos/include/freertos/portmacro.h
components/freertos/tasks.c

index d2b24e99523f9b722176adfc194bdb4bab49b35f..328ef36e26cea3c163101eb7fb60d26acb5e615a 100644 (file)
@@ -59,6 +59,10 @@ void esp_vApplicationIdleHook()
 #ifdef CONFIG_PM_ENABLE
     esp_pm_impl_idle_hook();
 #endif
+}
+
+extern void esp_vApplicationWaitiHook( void )
+{
     asm("waiti 0");
 }
 
index 11a4d3c0db4dc5f1026b8f3815058d2a4d3cef1c..f75b3fe8115aed4c189695ca8d9c7a08b922df11 100644 (file)
 #include "esp_pm.h"
 #include "esp_log.h"
 #include "esp_crosscore_int.h"
+#include "esp_clk.h"
 
 #include "soc/rtc.h"
 
 #include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
 #include "freertos/xtensa_timer.h"
 #include "xtensa/core-macros.h"
 
  */
 #define CCOMPARE_UPDATE_TIMEOUT 1000000
 
+/* When changing CCOMPARE, don't allow changes if the difference is less
+ * than this. This is to prevent setting CCOMPARE below CCOUNT.
+ */
+#define CCOMPARE_MIN_CYCLES_IN_FUTURE 1000
+
+/* When light sleep is used, wake this number of microseconds earlier than
+ * the next tick.
+ */
+#define LIGHT_SLEEP_EARLY_WAKEUP_US 100
+
 #ifdef CONFIG_PM_PROFILING
 #define WITH_PROFILING
 #endif
@@ -107,7 +119,7 @@ static const char* s_freq_names[] __attribute__((unused)) = {
         [RTC_CPU_FREQ_2M] = "2"
 };
 
-/* Whether automatic light sleep is enabled. Currently always false */
+/* Whether automatic light sleep is enabled */
 static bool s_light_sleep_en = false;
 
 /* When configuration is changed, current frequency may not match the
@@ -177,9 +189,11 @@ esp_err_t esp_pm_configure(const void* vconfig)
 #endif
 
     const esp_pm_config_esp32_t* config = (const esp_pm_config_esp32_t*) vconfig;
+#ifndef CONFIG_FREERTOS_USE_TICKLESS_IDLE
     if (config->light_sleep_enable) {
         return ESP_ERR_NOT_SUPPORTED;
     }
+#endif
 
     if (config->min_cpu_freq == RTC_CPU_FREQ_2M) {
         /* Minimal APB frequency to achieve 1MHz REF_TICK frequency is 5 MHz */
@@ -401,10 +415,9 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode)
  */
 static void IRAM_ATTR update_ccompare()
 {
-    const uint32_t ccompare_min_cycles_in_future = 1000;
     uint32_t ccount = XTHAL_GET_CCOUNT();
     uint32_t ccompare = XTHAL_GET_CCOMPARE(XT_TIMER_INDEX);
-    if ((ccompare - ccompare_min_cycles_in_future) - ccount < UINT32_MAX / 2) {
+    if ((ccompare - CCOMPARE_MIN_CYCLES_IN_FUTURE) - ccount < UINT32_MAX / 2) {
         uint32_t diff = ccompare - ccount;
         uint32_t diff_scaled = (diff * s_ccount_mul + s_ccount_div - 1) / s_ccount_div;
         if (diff_scaled < _xt_tick_divisor) {
@@ -453,6 +466,56 @@ void IRAM_ATTR esp_pm_impl_isr_hook()
     ESP_PM_TRACE_EXIT(ISR_HOOK, core_id);
 }
 
+#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
+
+bool IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
+{
+    bool result = false;
+    portENTER_CRITICAL(&s_switch_lock);
+    if (s_mode == PM_MODE_LIGHT_SLEEP && !s_is_switching) {
+        /* Calculate how much we can sleep */
+        int64_t next_esp_timer_alarm = esp_timer_get_next_alarm();
+        int64_t now = esp_timer_get_time();
+        int64_t time_until_next_alarm = next_esp_timer_alarm - now;
+        int64_t wakeup_delay_us = portTICK_PERIOD_MS * 1000LL * xExpectedIdleTime;
+        int64_t sleep_time_us = MIN(wakeup_delay_us, time_until_next_alarm);
+        if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL) {
+            esp_sleep_enable_timer_wakeup(sleep_time_us - LIGHT_SLEEP_EARLY_WAKEUP_US);
+#ifdef CONFIG_PM_TRACE
+            /* to force tracing GPIOs to keep state */
+            esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
+#endif
+            /* Enter sleep */
+            int core_id = xPortGetCoreID();
+            ESP_PM_TRACE_ENTER(SLEEP, core_id);
+            int64_t sleep_start = esp_timer_get_time();
+            esp_light_sleep_start();
+            int64_t slept_us = esp_timer_get_time() - sleep_start;
+            ESP_PM_TRACE_EXIT(SLEEP, core_id);
+
+            uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL);
+            if (slept_ticks > 0) {
+                /* Adjust RTOS tick count based on the amount of time spent in sleep */
+                vTaskStepTick(slept_ticks);
+
+                /* Trigger tick interrupt, since sleep time was longer
+                 * than portTICK_PERIOD_MS. Note that setting INTSET does not
+                 * work for timer interrupt, and changing CCOMPARE would clear
+                 * the interrupt flag.
+                 */
+                XTHAL_SET_CCOUNT(XTHAL_GET_CCOMPARE(XT_TIMER_INDEX) - 16);
+                while (!(XTHAL_GET_INTERRUPT() & BIT(XT_TIMER_INTNUM))) {
+                    ;
+                }
+            }
+            result = true;
+        }
+    }
+    portEXIT_CRITICAL(&s_switch_lock);
+    return result;
+}
+#endif //CONFIG_FREERTOS_USE_TICKLESS_IDLE
+
 #ifdef WITH_PROFILING
 void esp_pm_impl_dump_stats(FILE* out)
 {
index c1f240017e3bf8cd3300f17ce9bafe3f0af7fc69..eddaec38435efefb92398d8e9fec1a567a76c15d 100644 (file)
@@ -27,6 +27,7 @@ static const int DRAM_ATTR s_trace_io[] = {
         BIT(18), BIT(18), // ESP_PM_TRACE_FREQ_SWITCH
         BIT(19), BIT(19), // ESP_PM_TRACE_CCOMPARE_UPDATE
         BIT(25), BIT(26), // ESP_PM_TRACE_ISR_HOOK
+        BIT(27), BIT(27), // ESP_PM_TRACE_SLEEP
 };
 
 void esp_pm_trace_init()
index 1aff1ac806d728f25b0a13fa3b902cac751188cc..20eebb0d692bf72fcaa7fcc1ddd4a20b088894bd 100644 (file)
@@ -22,6 +22,7 @@ typedef enum {
     ESP_PM_TRACE_FREQ_SWITCH,
     ESP_PM_TRACE_CCOMPARE_UPDATE,
     ESP_PM_TRACE_ISR_HOOK,
+    ESP_PM_TRACE_SLEEP,
     ESP_PM_TRACE_TYPE_MAX
 } esp_pm_trace_event_t;
 
index 7033238a04dda445695e03db52af8cd84e5d4e3a..fa432eddc99a9efdc1fc2d18352ef3c16449701b 100644 (file)
@@ -7,7 +7,14 @@
 #include "esp_clk.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
+#include "freertos/semphr.h"
 #include "esp_log.h"
+#include "driver/timer.h"
+#include "driver/rtc_io.h"
+#include "esp32/ulp.h"
+#include "soc/rtc_io_reg.h"
+#include "soc/rtc_cntl_reg.h"
+#include "soc/rtc_gpio_channel.h"
 
 TEST_CASE("Can dump power management lock stats", "[pm]")
 {
@@ -48,4 +55,256 @@ TEST_CASE("Can switch frequency using esp_pm_configure", "[pm]")
     switch_freq(orig_freq_mhz);
 }
 
+#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
+
+static void light_sleep_enable()
+{
+    const esp_pm_config_esp32_t pm_config = {
+        .max_cpu_freq = rtc_clk_cpu_freq_get(),
+        .min_cpu_freq = RTC_CPU_FREQ_XTAL,
+        .light_sleep_enable = true
+    };
+    ESP_ERROR_CHECK( esp_pm_configure(&pm_config) );
+}
+
+static void light_sleep_disable()
+{
+    const esp_pm_config_esp32_t pm_config = {
+        .max_cpu_freq = rtc_clk_cpu_freq_get(),
+        .min_cpu_freq = rtc_clk_cpu_freq_get(),
+    };
+    ESP_ERROR_CHECK( esp_pm_configure(&pm_config) );
+}
+
+TEST_CASE("Automatic light occurs when tasks are suspended", "[pm]")
+{
+    /* To figure out if light sleep takes place, use Timer Group timer.
+     * It will stop working while in light sleep.
+     */
+    timer_config_t config = {
+        .counter_dir = TIMER_COUNT_UP,
+        .divider = 80 /* 1 us per tick */
+    };
+    timer_init(TIMER_GROUP_0, TIMER_0, &config);
+    timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0);
+    timer_start(TIMER_GROUP_0, TIMER_0);
+
+    light_sleep_enable();
+
+    for (int ticks_to_delay = CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP;
+            ticks_to_delay < CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP * 10;
+            ++ticks_to_delay) {
+
+        /* Wait until next tick */
+        vTaskDelay(1);
+
+        /* The following delay should cause light sleep to start */
+        uint64_t count_start;
+        timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &count_start);
+        vTaskDelay(ticks_to_delay);
+        uint64_t count_end;
+        timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &count_end);
+
+
+        int timer_diff_us = (int) (count_end - count_start);
+        const int us_per_tick = 1 * portTICK_PERIOD_MS * 1000;
+
+        printf("%d %d\n", ticks_to_delay * us_per_tick, timer_diff_us);
+        TEST_ASSERT(timer_diff_us < ticks_to_delay * us_per_tick);
+    }
+
+    light_sleep_disable();
+}
+
+
+TEST_CASE("Can wake up from automatic light sleep by GPIO", "[pm]")
+{
+    assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 16 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig");
+
+    /* Set up GPIO used to wake up RTC */
+    const int ext1_wakeup_gpio = 25;
+    const int ext_rtc_io = RTCIO_GPIO25_CHANNEL;
+    TEST_ESP_OK(rtc_gpio_init(ext1_wakeup_gpio));
+    rtc_gpio_set_direction(ext1_wakeup_gpio, RTC_GPIO_MODE_INPUT_OUTPUT);
+    rtc_gpio_set_level(ext1_wakeup_gpio, 0);
+
+   /* Enable wakeup */
+    TEST_ESP_OK(esp_sleep_enable_ext1_wakeup(1ULL << ext1_wakeup_gpio, ESP_EXT1_WAKEUP_ANY_HIGH));
+
+    /* To simplify test environment, we'll use a ULP program to set GPIO high */
+    ulp_insn_t ulp_code[] = {
+            I_DELAY(65535), /* about 8ms, given 8MHz ULP clock */
+            I_WR_REG_BIT(RTC_CNTL_HOLD_FORCE_REG, RTC_CNTL_PDAC1_HOLD_FORCE_S, 0),
+            I_WR_REG_BIT(RTC_GPIO_OUT_REG, ext_rtc_io + RTC_GPIO_OUT_DATA_S, 1),
+            I_DELAY(1000),
+            I_WR_REG_BIT(RTC_GPIO_OUT_REG, ext_rtc_io + RTC_GPIO_OUT_DATA_S, 0),
+            I_WR_REG_BIT(RTC_CNTL_HOLD_FORCE_REG, RTC_CNTL_PDAC1_HOLD_FORCE_S, 1),
+            I_END(),
+            I_HALT()
+    };
+    TEST_ESP_OK(ulp_set_wakeup_period(0, 1000 /* us */));
+    size_t size = sizeof(ulp_code)/sizeof(ulp_insn_t);
+    TEST_ESP_OK(ulp_process_macros_and_load(0, ulp_code, &size));
+
+    light_sleep_enable();
+
+    for (int i = 0; i < 10; ++i) {
+        /* Set GPIO low */
+        REG_CLR_BIT(rtc_gpio_desc[ext1_wakeup_gpio].reg, rtc_gpio_desc[ext1_wakeup_gpio].hold_force);
+        rtc_gpio_set_level(ext1_wakeup_gpio, 0);
+        REG_SET_BIT(rtc_gpio_desc[ext1_wakeup_gpio].reg, rtc_gpio_desc[ext1_wakeup_gpio].hold_force);
+
+        /* Wait for the next tick */
+        vTaskDelay(1);
+
+        /* Start ULP program */
+        ulp_run(0);
+
+        const int delay_ms = 200;
+        const int delay_ticks = delay_ms / portTICK_PERIOD_MS;
+
+        int64_t start_rtc = esp_clk_rtc_time();
+        int64_t start_hs = esp_timer_get_time();
+        uint32_t start_tick = xTaskGetTickCount();
+        /* Will enter sleep here */
+        vTaskDelay(delay_ticks);
+        int64_t end_rtc = esp_clk_rtc_time();
+        int64_t end_hs = esp_timer_get_time();
+        uint32_t end_tick = xTaskGetTickCount();
+
+        printf("%lld %lld %u\n", end_rtc - start_rtc, end_hs - start_hs, end_tick - start_tick);
+
+        TEST_ASSERT_INT32_WITHIN(3, delay_ticks, end_tick - start_tick);
+        TEST_ASSERT_INT32_WITHIN(2 * portTICK_PERIOD_MS * 1000, delay_ms * 1000, end_hs - start_hs);
+        TEST_ASSERT_INT32_WITHIN(2 * portTICK_PERIOD_MS * 1000, delay_ms * 1000, end_rtc - start_rtc);
+    }
+    REG_CLR_BIT(rtc_gpio_desc[ext1_wakeup_gpio].reg, rtc_gpio_desc[ext1_wakeup_gpio].hold_force);
+    rtc_gpio_deinit(ext1_wakeup_gpio);
+
+    light_sleep_disable();
+}
+
+
+typedef struct {
+    int delay_us;
+    int result;
+    SemaphoreHandle_t done;
+} delay_test_arg_t;
+
+static void test_delay_task(void* p)
+{
+    delay_test_arg_t* arg = (delay_test_arg_t*) p;
+    vTaskDelay(1);
+
+    uint64_t start = esp_clk_rtc_time();
+    vTaskDelay(arg->delay_us / portTICK_PERIOD_MS / 1000);
+    uint64_t stop = esp_clk_rtc_time();
+
+    arg->result = (int) (stop - start);
+    xSemaphoreGive(arg->done);
+    vTaskDelete(NULL);
+}
+
+TEST_CASE("vTaskDelay duration is correct with light sleep enabled", "[pm]")
+{
+    light_sleep_enable();
+
+    delay_test_arg_t args = {
+            .done = xSemaphoreCreateBinary()
+    };
+
+    const int delays[] = { 10, 20, 50, 100, 150, 200, 250 };
+    const int delays_count = sizeof(delays) / sizeof(delays[0]);
+
+    for (int i = 0; i < delays_count; ++i) {
+        int delay_ms = delays[i];
+        args.delay_us = delay_ms * 1000;
+
+        xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 0);
+        TEST_ASSERT( xSemaphoreTake(args.done, delay_ms * 10 / portTICK_PERIOD_MS) );
+        printf("CPU0: %d %d\n", args.delay_us, args.result);
+        TEST_ASSERT_INT32_WITHIN(1000 * portTICK_PERIOD_MS * 2, args.delay_us, args.result);
+
+#if portNUM_PROCESSORS == 2
+        xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 1);
+        TEST_ASSERT( xSemaphoreTake(args.done, delay_ms * 10 / portTICK_PERIOD_MS) );
+        printf("CPU1: %d %d\n", args.delay_us, args.result);
+        TEST_ASSERT_INT32_WITHIN(1000 * portTICK_PERIOD_MS * 2, args.delay_us, args.result);
+#endif
+    }
+    vSemaphoreDelete(args.done);
+
+    light_sleep_disable();
+}
+
+/* This test is similar to the one in test_esp_timer.c, but since we can't use
+ * ref_clock, this test uses RTC clock for timing. Also enables automatic
+ * light sleep.
+ */
+TEST_CASE("esp_timer produces correct delays with light sleep", "[pm]")
+{
+    // no, we can't make this a const size_t (ยง6.7.5.2)
+#define NUM_INTERVALS 16
+
+    typedef struct {
+        esp_timer_handle_t timer;
+        size_t cur_interval;
+        int intervals[NUM_INTERVALS];
+        int64_t t_start;
+        SemaphoreHandle_t done;
+    } test_args_t;
+
+    void timer_func(void* arg)
+    {
+        test_args_t* p_args = (test_args_t*) arg;
+        int64_t t_end = esp_clk_rtc_time();
+        int32_t ms_diff = (t_end - p_args->t_start) / 1000;
+        printf("timer #%d %dms\n", p_args->cur_interval, ms_diff);
+        p_args->intervals[p_args->cur_interval++] = ms_diff;
+        // Deliberately make timer handler run longer.
+        // We check that this doesn't affect the result.
+        ets_delay_us(10*1000);
+        if (p_args->cur_interval == NUM_INTERVALS) {
+            printf("done\n");
+            TEST_ESP_OK(esp_timer_stop(p_args->timer));
+            xSemaphoreGive(p_args->done);
+        }
+    }
+
+    light_sleep_enable();
+
+    const int delay_ms = 100;
+    test_args_t args = {0};
+    esp_timer_handle_t timer1;
+    esp_timer_create_args_t create_args = {
+            .callback = &timer_func,
+            .arg = &args,
+            .name = "timer1",
+    };
+    TEST_ESP_OK(esp_timer_create(&create_args, &timer1));
+
+    args.timer = timer1;
+    args.t_start = esp_clk_rtc_time();
+    args.done = xSemaphoreCreateBinary();
+    TEST_ESP_OK(esp_timer_start_periodic(timer1, delay_ms * 1000));
+
+    TEST_ASSERT(xSemaphoreTake(args.done, delay_ms * NUM_INTERVALS * 2));
+
+    TEST_ASSERT_EQUAL_UINT32(NUM_INTERVALS, args.cur_interval);
+    for (size_t i = 0; i < NUM_INTERVALS; ++i) {
+        TEST_ASSERT_INT32_WITHIN(portTICK_PERIOD_MS, (i + 1) * delay_ms, args.intervals[i]);
+    }
+
+    TEST_ESP_OK( esp_timer_dump(stdout) );
+
+    TEST_ESP_OK( esp_timer_delete(timer1) );
+    vSemaphoreDelete(args.done);
+
+    light_sleep_disable();
+
+#undef NUM_INTERVALS
+}
+
+#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
+
 #endif // CONFIG_PM_ENABLE
index c469d679d9f66804d268b8b5e32ca4e1298e3054..1a037377c8f595c431a04a33f8c88096bdaedab9 100644 (file)
@@ -357,6 +357,31 @@ config FREERTOS_RUN_TIME_STATS_USING_CPU_CLK
 
 endchoice
 
+config FREERTOS_USE_TICKLESS_IDLE
+    bool "Tickless idle support"
+    depends on PM_ENABLE
+    default n
+    help
+        If power management support is enabled, FreeRTOS will be able to put
+        the system into light sleep mode when no tasks need to run for a number
+        of ticks. This number can be set using FREERTOS_IDLE_TIME_BEFORE_SLEEP option.
+        This feature is also known as "automatic light sleep".
+
+        Note that timers created using esp_timer APIs may prevent the system from
+        entering sleep mode, even when no tasks need to run.
+
+        If disabled, automatic light sleep support will be disabled.
+
+config FREERTOS_IDLE_TIME_BEFORE_SLEEP
+    int "Minimum number of ticks to enter sleep mode for"
+    depends on FREERTOS_USE_TICKLESS_IDLE
+    default 3
+    range 2 4294967295
+    # Minimal value is 2 because of a check in FreeRTOS.h (search configEXPECTED_IDLE_TIME_BEFORE_SLEEP)
+    help
+        FreeRTOS will enter light sleep mode if no tasks need to run for this number
+        of ticks.
+
 menuconfig FREERTOS_DEBUG_INTERNALS
     bool "Debug FreeRTOS internals"
     default n
index 37912a66ce13976ccab5f2af4a662236238e69cc..c52bc927cf4ac0ca023b5b4c9180c0f27b49110d 100644 (file)
@@ -289,6 +289,10 @@ extern void vPortCleanUpTCB ( void *pxTCB );
 #define INCLUDE_eTaskGetState               1
 #define configUSE_QUEUE_SETS                1
 
+#define configUSE_TICKLESS_IDLE             CONFIG_FREERTOS_USE_TICKLESS_IDLE
+#if configUSE_TICKLESS_IDLE
+#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP   CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP
+#endif //configUSE_TICKLESS_IDLE
 
 #define configXT_BOARD                      1   /* Board mode */
 #define configXT_SIMULATOR                                     0
index 93d2071ba540a0a7d0c542e95b8935e0e6c240f5..df5c071d17f1d998e87f4fcb981ab85a07e3fbe3 100644 (file)
@@ -365,9 +365,13 @@ typedef struct {
        #define PRIVILEGED_DATA
 #endif
 
+extern void esp_vApplicationIdleHook( void );
+extern void esp_vApplicationWaitiHook( void );
 
 void _xt_coproc_release(volatile void * coproc_sa_base);
+bool vApplicationSleep( TickType_t xExpectedIdleTime );
 
+#define portSUPPRESS_TICKS_AND_SLEEP( idleTime ) vApplicationSleep( idleTime )
 
 // porttrace
 #if configUSE_TRACE_FACILITY_2
index 980699ffcea3eed0c0ff311185f0e3b8a4fdeaa5..fe081975dd475c59f6d1ad99b7d913cde4a873a8 100644 (file)
@@ -2151,6 +2151,23 @@ void vTaskSuspendAll( void )
 
 #if ( configUSE_TICKLESS_IDLE != 0 )
 
+       static BaseType_t xHaveReadyTasks()
+       {
+               for (int i = tskIDLE_PRIORITY + 1; i < configMAX_PRIORITIES; ++i)
+               {
+                       if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ i ] ) ) > 0 )
+                       {
+                               return pdTRUE;
+                       }
+                       else
+                       {
+                               mtCOVERAGE_TEST_MARKER();
+                       }
+               }
+               return pdFALSE;
+       }
+
+
        static TickType_t prvGetExpectedIdleTime( void )
        {
        TickType_t xReturn;
@@ -2161,7 +2178,18 @@ void vTaskSuspendAll( void )
                {
                        xReturn = 0;
                }
-               else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 )
+#if portNUM_PROCESSORS > 1
+               /* This function is called from Idle task; in single core case this
+                * means that no higher priority tasks are ready to run, and we can
+                * enter sleep. In SMP case, there might be ready tasks waiting for
+                * the other CPU, so need to check all ready lists.
+                */
+               else if( xHaveReadyTasks() )
+               {
+                       xReturn = 0;
+               }
+#endif // portNUM_PROCESSORS > 1
+               else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > portNUM_PROCESSORS )
                {
                        /* There are other idle priority tasks in the ready state.  If
                        time slicing is used then the very next tick interrupt must be
@@ -3405,7 +3433,6 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
                #endif /* configUSE_IDLE_HOOK */
                {
                        /* Call the esp-idf hook system */
-                       extern void esp_vApplicationIdleHook( void );
                        esp_vApplicationIdleHook();
                }
 
@@ -3417,6 +3444,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
                #if ( configUSE_TICKLESS_IDLE != 0 )
                {
                TickType_t xExpectedIdleTime;
+               BaseType_t xEnteredSleep = pdFALSE;
 
                        /* It is not desirable to suspend then resume the scheduler on
                        each iteration of the idle task.  Therefore, a preliminary
@@ -3427,7 +3455,6 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
 
                        if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
                        {
-//                             vTaskSuspendAll();
                                taskENTER_CRITICAL(&xTaskQueueMutex);
                                {
                                        /* Now the scheduler is suspended, the expected idle
@@ -3439,7 +3466,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
                                        if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
                                        {
                                                traceLOW_POWER_IDLE_BEGIN();
-                                               portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
+                                               xEnteredSleep = portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
                                                traceLOW_POWER_IDLE_END();
                                        }
                                        else
@@ -3448,13 +3475,21 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
                                        }
                                }
                                taskEXIT_CRITICAL(&xTaskQueueMutex);
-//                             ( void ) xTaskResumeAll();
                        }
                        else
                        {
                                mtCOVERAGE_TEST_MARKER();
                        }
+                       /* It might be possible to enter tickless idle again, so skip
+                        * the fallback sleep hook if tickless idle was successful
+                        */
+                       if ( !xEnteredSleep )
+                       {
+                               esp_vApplicationWaitiHook();
+                       }
                }
+               #else
+               esp_vApplicationWaitiHook();
                #endif /* configUSE_TICKLESS_IDLE */
        }
 }