From d2d3269159870a8c848d8627c5696a9a1904d347 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 12 Feb 2020 12:41:52 +0100 Subject: [PATCH] esp32s2: sync esp_pm code from esp32 --- components/esp32s2/cpu_start.c | 9 +- components/esp32s2/include/esp32s2/pm.h | 6 +- components/esp32s2/pm_esp32s2.c | 244 +++++++++++++----------- components/esp32s2/pm_trace.c | 12 +- 4 files changed, 141 insertions(+), 130 deletions(-) diff --git a/components/esp32s2/cpu_start.c b/components/esp32s2/cpu_start.c index 4d1d37e89..f25cb3b07 100644 --- a/components/esp32s2/cpu_start.c +++ b/components/esp32s2/cpu_start.c @@ -317,11 +317,10 @@ void start_cpu0_default(void) #ifdef CONFIG_PM_ENABLE esp_pm_impl_init(); #ifdef CONFIG_PM_DFS_INIT_AUTO - rtc_cpu_freq_t max_freq; - rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ, &max_freq); - esp_pm_config_esp32_t cfg = { - .max_cpu_freq = max_freq, - .min_cpu_freq = RTC_CPU_FREQ_XTAL + int xtal_freq = (int) rtc_clk_xtal_freq_get(); + esp_pm_config_esp32s2_t cfg = { + .max_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ, + .min_freq_mhz = xtal_freq, }; esp_pm_configure(&cfg); #endif //CONFIG_PM_DFS_INIT_AUTO diff --git a/components/esp32s2/include/esp32s2/pm.h b/components/esp32s2/include/esp32s2/pm.h index a7cbf0eac..dac31e0c9 100644 --- a/components/esp32s2/include/esp32s2/pm.h +++ b/components/esp32s2/include/esp32s2/pm.h @@ -31,10 +31,10 @@ extern "C" { * Pass a pointer to this structure as an argument to esp_pm_configure function. */ typedef struct { - rtc_cpu_freq_t max_cpu_freq; /*!< Maximum CPU frequency to use */ - rtc_cpu_freq_t min_cpu_freq; /*!< Minimum CPU frequency to use when no frequency locks are taken */ + int max_freq_mhz; /*!< Maximum CPU frequency, in MHz */ + int min_freq_mhz; /*!< Minimum CPU frequency to use when no locks are taken, in MHz */ bool light_sleep_enable; /*!< Enter light sleep when no locks are taken */ -} esp_pm_config_esp32_t; +} esp_pm_config_esp32s2_t; #ifdef __cplusplus diff --git a/components/esp32s2/pm_esp32s2.c b/components/esp32s2/pm_esp32s2.c index 7368c7b90..2bfd4c351 100644 --- a/components/esp32s2/pm_esp32s2.c +++ b/components/esp32s2/pm_esp32s2.c @@ -1,4 +1,4 @@ -// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD +// Copyright 2016-2020 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. @@ -35,6 +35,7 @@ #include "esp_private/pm_trace.h" #include "esp_private/esp_timer_private.h" #include "esp32s2/pm.h" +#include "esp_sleep.h" /* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work * for the purpose of detecting a deadlock. @@ -51,6 +52,9 @@ */ #define LIGHT_SLEEP_EARLY_WAKEUP_US 100 +/* Minimal divider at which REF_CLK_FREQ can be obtained */ +#define REF_CLK_DIV_MIN 2 + #ifdef CONFIG_PM_PROFILING #define WITH_PROFILING #endif @@ -75,49 +79,42 @@ static uint32_t s_mode_mask; static uint32_t s_ccount_div; static uint32_t s_ccount_mul; +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE +/* 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. + */ +static bool s_skipped_light_sleep[portNUM_PROCESSORS]; + +#if portNUM_PROCESSORS == 2 +/* When light sleep is finished on one CPU, it is possible that the other CPU + * will enter light sleep again very soon, before interrupts on the first CPU + * get a chance to run. To avoid such situation, set a flag for the other CPU to + * skip light sleep attempt. + */ +static bool s_skip_light_sleep[portNUM_PROCESSORS]; +#endif // portNUM_PROCESSORS == 2 +#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE + /* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU. * Used in conjunction with cross-core interrupt to update CCOMPARE on the other CPU. */ static volatile bool s_need_update_ccompare[portNUM_PROCESSORS]; -/* When no RTOS tasks are active, these locks are released to allow going into - * a lower power mode. Used by ISR hook and idle hook. - */ -static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS]; - /* A flag indicating that Idle hook has run on a given CPU; * Next interrupt on the same CPU will take s_rtos_lock_handle. */ static bool s_core_idle[portNUM_PROCESSORS]; -/* g_ticks_us defined in ROM for PRO CPU */ -extern uint32_t g_ticks_per_us_pro; +/* When no RTOS tasks are active, these locks are released to allow going into + * a lower power mode. Used by ISR hook and idle hook. + */ +static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS]; -/* Lookup table of CPU frequencies to be used in each mode. +/* Lookup table of CPU frequency configs to be used in each mode. * Initialized by esp_pm_impl_init and modified by esp_pm_configure. */ -rtc_cpu_freq_t s_cpu_freq_by_mode[PM_MODE_COUNT]; - -/* Lookup table of CPU ticks per microsecond for each RTC_CPU_FREQ_ value. - * Essentially the same as returned by rtc_clk_cpu_freq_value(), but without - * the function call. Not const because XTAL frequency is only known at run time. - */ -static uint32_t s_cpu_freq_to_ticks[] = { - [RTC_CPU_FREQ_XTAL] = 0, /* This is set by esp_pm_impl_init */ - [RTC_CPU_FREQ_80M] = 80, - [RTC_CPU_FREQ_160M] = 160, - [RTC_CPU_FREQ_240M] = 240, - [RTC_CPU_FREQ_2M] = 2 -}; - -/* Lookup table of names for each RTC_CPU_FREQ_ value. Used for logging only. */ -static const char* s_freq_names[] __attribute__((unused)) = { - [RTC_CPU_FREQ_XTAL] = "XTAL", - [RTC_CPU_FREQ_80M] = "80", - [RTC_CPU_FREQ_160M] = "160", - [RTC_CPU_FREQ_240M] = "240", - [RTC_CPU_FREQ_2M] = "2" -}; +rtc_cpu_freq_config_t s_cpu_freq_by_mode[PM_MODE_COUNT]; /* Whether automatic light sleep is enabled */ static bool s_light_sleep_en = false; @@ -144,7 +141,7 @@ static const char* s_mode_names[] = { #endif // WITH_PROFILING -static const char* TAG = "pm_esp32"; +static const char* TAG = "pm_esp32s2"; static void update_ccompare(void); static void do_switch(pm_mode_t new_mode); @@ -167,74 +164,62 @@ pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg) } } -/* rtc_cpu_freq_t enum is not ordered by frequency, so convert to MHz, - * figure out the maximum value, then convert back to rtc_cpu_freq_t. - */ -static rtc_cpu_freq_t max_freq_of(rtc_cpu_freq_t f1, rtc_cpu_freq_t f2) -{ - int f1_hz = rtc_clk_cpu_freq_value(f1); - int f2_hz = rtc_clk_cpu_freq_value(f2); - int f_max_hz = MAX(f1_hz, f2_hz); - rtc_cpu_freq_t result = RTC_CPU_FREQ_XTAL; - if (!rtc_clk_cpu_freq_from_mhz(f_max_hz/1000000, &result)) { - assert(false && "unsupported frequency"); - } - return result; -} - esp_err_t esp_pm_configure(const void* vconfig) { #ifndef CONFIG_PM_ENABLE return ESP_ERR_NOT_SUPPORTED; #endif - const esp_pm_config_esp32_t* config = (const esp_pm_config_esp32_t*) vconfig; + const esp_pm_config_esp32s2_t* config = (const esp_pm_config_esp32s2_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 */ - return ESP_ERR_NOT_SUPPORTED; - } + int min_freq_mhz = config->min_freq_mhz; + int max_freq_mhz = config->max_freq_mhz; - rtc_cpu_freq_t min_freq = config->min_cpu_freq; - rtc_cpu_freq_t max_freq = config->max_cpu_freq; - int min_freq_mhz = rtc_clk_cpu_freq_value(min_freq); - int max_freq_mhz = rtc_clk_cpu_freq_value(max_freq); if (min_freq_mhz > max_freq_mhz) { return ESP_ERR_INVALID_ARG; } - rtc_cpu_freq_t apb_max_freq = max_freq; /* CPU frequency in APB_MAX mode */ - if (max_freq == RTC_CPU_FREQ_240M) { - /* We can't switch between 240 and 80/160 without disabling PLL, - * so use 240MHz CPU frequency when 80MHz APB frequency is requested. - */ - apb_max_freq = RTC_CPU_FREQ_240M; - } else if (max_freq == RTC_CPU_FREQ_160M || max_freq == RTC_CPU_FREQ_80M) { - /* Otherwise, can use 80MHz - * CPU frequency when 80MHz APB frequency is requested. - */ - apb_max_freq = RTC_CPU_FREQ_80M; + rtc_cpu_freq_config_t freq_config; + if (!rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &freq_config)) { + ESP_LOGW(TAG, "invalid min_freq_mhz value (%d)", min_freq_mhz); + return ESP_ERR_INVALID_ARG; } - apb_max_freq = max_freq_of(apb_max_freq, min_freq); + int xtal_freq_mhz = (int) rtc_clk_xtal_freq_get(); + if (min_freq_mhz < xtal_freq_mhz && min_freq_mhz * MHZ / REF_CLK_FREQ < REF_CLK_DIV_MIN) { + ESP_LOGW(TAG, "min_freq_mhz should be >= %d", REF_CLK_FREQ * REF_CLK_DIV_MIN / MHZ); + return ESP_ERR_INVALID_ARG; + } + + if (!rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &freq_config)) { + ESP_LOGW(TAG, "invalid max_freq_mhz value (%d)", max_freq_mhz); + return ESP_ERR_INVALID_ARG; + } + + int apb_max_freq = MIN(max_freq_mhz, 80); /* CPU frequency in APB_MAX mode */ + apb_max_freq = MAX(apb_max_freq, min_freq_mhz); ESP_LOGI(TAG, "Frequency switching config: " - "CPU_MAX: %s, APB_MAX: %s, APB_MIN: %s, Light sleep: %s", - s_freq_names[max_freq], - s_freq_names[apb_max_freq], - s_freq_names[min_freq], + "CPU_MAX: %d, APB_MAX: %d, APB_MIN: %d, Light sleep: %s", + max_freq_mhz, + apb_max_freq, + min_freq_mhz, config->light_sleep_enable ? "ENABLED" : "DISABLED"); portENTER_CRITICAL(&s_switch_lock); - s_cpu_freq_by_mode[PM_MODE_CPU_MAX] = max_freq; - s_cpu_freq_by_mode[PM_MODE_APB_MAX] = apb_max_freq; - s_cpu_freq_by_mode[PM_MODE_APB_MIN] = min_freq; - s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = min_freq; + bool res; + res = rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_CPU_MAX]); + assert(res); + res = rtc_clk_cpu_freq_mhz_to_config(apb_max_freq, &s_cpu_freq_by_mode[PM_MODE_APB_MAX]); + assert(res); + res = rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_APB_MIN]); + assert(res); + s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = s_cpu_freq_by_mode[PM_MODE_APB_MIN]; s_light_sleep_en = config->light_sleep_enable; s_config_changed = true; portEXIT_CRITICAL(&s_switch_lock); @@ -261,7 +246,7 @@ void IRAM_ATTR esp_pm_impl_switch_mode(pm_mode_t mode, { bool need_switch = false; uint32_t mode_mask = BIT(mode); - portENTER_CRITICAL(&s_switch_lock); + portENTER_CRITICAL_SAFE(&s_switch_lock); uint32_t count; if (lock_or_unlock == MODE_LOCK) { count = ++s_mode_lock_counts[mode]; @@ -288,7 +273,7 @@ void IRAM_ATTR esp_pm_impl_switch_mode(pm_mode_t mode, s_last_mode_change_time = now; #endif // WITH_PROFILING } - portEXIT_CRITICAL(&s_switch_lock); + portEXIT_CRITICAL_SAFE(&s_switch_lock); if (need_switch && new_mode != s_mode) { do_switch(new_mode); } @@ -310,7 +295,7 @@ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_p } /* Calculate new tick divisor */ - _xt_tick_divisor = ticks_per_us * 1000000 / XT_TICK_PER_SEC; + _xt_tick_divisor = ticks_per_us * MHZ / XT_TICK_PER_SEC; int core_id = xPortGetCoreID(); if (s_rtos_lock_handle[core_id] != NULL) { @@ -375,17 +360,18 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode) s_config_changed = false; portEXIT_CRITICAL_ISR(&s_switch_lock); - rtc_cpu_freq_t new_freq = s_cpu_freq_by_mode[new_mode]; - rtc_cpu_freq_t old_freq; + rtc_cpu_freq_config_t new_config = s_cpu_freq_by_mode[new_mode]; + rtc_cpu_freq_config_t old_config; + if (!config_changed) { - old_freq = s_cpu_freq_by_mode[s_mode]; + old_config = s_cpu_freq_by_mode[s_mode]; } else { - old_freq = rtc_clk_cpu_freq_get(); + rtc_clk_cpu_freq_get_config(&old_config); } - if (new_freq != old_freq) { - uint32_t old_ticks_per_us = g_ticks_per_us_pro; - uint32_t new_ticks_per_us = s_cpu_freq_to_ticks[new_freq]; + if (new_config.freq_mhz != old_config.freq_mhz) { + uint32_t old_ticks_per_us = old_config.freq_mhz; + uint32_t new_ticks_per_us = new_config.freq_mhz; bool switch_down = new_ticks_per_us < old_ticks_per_us; @@ -393,7 +379,7 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode) if (switch_down) { on_freq_update(old_ticks_per_us, new_ticks_per_us); } - rtc_clk_cpu_freq_set_fast(new_freq); + rtc_clk_cpu_freq_set_config_fast(&new_config); if (!switch_down) { on_freq_update(old_ticks_per_us, new_ticks_per_us); } @@ -449,6 +435,28 @@ void esp_pm_impl_idle_hook(void) ESP_PM_TRACE_ENTER(IDLE, core_id); } +void IRAM_ATTR esp_pm_impl_isr_hook(void) +{ + int core_id = xPortGetCoreID(); + ESP_PM_TRACE_ENTER(ISR_HOOK, core_id); + /* Prevent higher level interrupts (than the one this function was called from) + * from happening in this section, since they will also call into esp_pm_impl_isr_hook. + */ + uint32_t state = portENTER_CRITICAL_NESTED(); +#if portNUM_PROCESSORS == 2 + if (s_need_update_ccompare[core_id]) { + update_ccompare(); + s_need_update_ccompare[core_id] = false; + } else { + leave_idle(); + } +#else + leave_idle(); +#endif // portNUM_PROCESSORS == 2 + portEXIT_CRITICAL_NESTED(state); + ESP_PM_TRACE_EXIT(ISR_HOOK, core_id); +} + void esp_pm_impl_waiti(void) { #if CONFIG_FREERTOS_USE_TICKLESS_IDLE @@ -467,30 +475,37 @@ void esp_pm_impl_waiti(void) #endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE } -void IRAM_ATTR esp_pm_impl_isr_hook(void) -{ - int core_id = xPortGetCoreID(); - ESP_PM_TRACE_ENTER(ISR_HOOK, core_id); -#if portNUM_PROCESSORS == 2 - if (s_need_update_ccompare[core_id]) { - update_ccompare(); - s_need_update_ccompare[core_id] = false; - } else { - leave_idle(); - } -#else - leave_idle(); -#endif // portNUM_PROCESSORS == 2 - ESP_PM_TRACE_EXIT(ISR_HOOK, core_id); -} - #if CONFIG_FREERTOS_USE_TICKLESS_IDLE -bool IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) +static inline bool IRAM_ATTR should_skip_light_sleep(int core_id) +{ +#if portNUM_PROCESSORS == 2 + if (s_skip_light_sleep[core_id]) { + s_skip_light_sleep[core_id] = false; + s_skipped_light_sleep[core_id] = true; + return true; + } +#endif // portNUM_PROCESSORS == 2 + if (s_mode != PM_MODE_LIGHT_SLEEP || s_is_switching) { + s_skipped_light_sleep[core_id] = true; + } else { + s_skipped_light_sleep[core_id] = false; + } + return s_skipped_light_sleep[core_id]; +} + +static inline void IRAM_ATTR other_core_should_skip_light_sleep(int core_id) +{ +#if portNUM_PROCESSORS == 2 + s_skip_light_sleep[!core_id] = true; +#endif +} + +void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) { - bool result = false; portENTER_CRITICAL(&s_switch_lock); - if (s_mode == PM_MODE_LIGHT_SLEEP && !s_is_switching) { + int core_id = xPortGetCoreID(); + if (!should_skip_light_sleep(core_id)) { /* Calculate how much we can sleep */ int64_t next_esp_timer_alarm = esp_timer_get_next_alarm(); int64_t now = esp_timer_get_time(); @@ -504,7 +519,6 @@ bool IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) 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(); @@ -526,11 +540,10 @@ bool IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) ; } } - result = true; + other_core_should_skip_light_sleep(core_id); } } portEXIT_CRITICAL(&s_switch_lock); - return result; } #endif //CONFIG_FREERTOS_USE_TICKLESS_IDLE @@ -554,9 +567,9 @@ void esp_pm_impl_dump_stats(FILE* out) /* don't display light sleep mode if it's not enabled */ continue; } - fprintf(out, "%8s %6s %12lld %2d%%\n", + fprintf(out, "%8s %3dM %12lld %2d%%\n", s_mode_names[i], - s_freq_names[s_cpu_freq_by_mode[i]], + s_cpu_freq_by_mode[i].freq_mhz, time_in_mode[i], (int) (time_in_mode[i] * 100 / now)); } @@ -565,7 +578,6 @@ void esp_pm_impl_dump_stats(FILE* out) void esp_pm_impl_init(void) { - s_cpu_freq_to_ticks[RTC_CPU_FREQ_XTAL] = rtc_clk_xtal_freq_get(); #ifdef CONFIG_PM_TRACE esp_pm_trace_init(); #endif @@ -581,11 +593,11 @@ void esp_pm_impl_init(void) /* Configure all modes to use the default CPU frequency. * This will be modified later by a call to esp_pm_configure. */ - rtc_cpu_freq_t default_freq; - if (!rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ, &default_freq)) { + rtc_cpu_freq_config_t default_config; + if (!rtc_clk_cpu_freq_mhz_to_config(CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ, &default_config)) { assert(false && "unsupported frequency"); } for (size_t i = 0; i < PM_MODE_COUNT; ++i) { - s_cpu_freq_by_mode[i] = default_freq; + s_cpu_freq_by_mode[i] = default_config; } } diff --git a/components/esp32s2/pm_trace.c b/components/esp32s2/pm_trace.c index cad2e40e2..db9dbfa2d 100644 --- a/components/esp32s2/pm_trace.c +++ b/components/esp32s2/pm_trace.c @@ -22,12 +22,12 @@ * Feel free to change when debugging. */ static const int DRAM_ATTR s_trace_io[] = { - BIT(4), BIT(5), // ESP_PM_TRACE_IDLE - BIT(16), BIT(17), // ESP_PM_TRACE_TICK - 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 + BIT(2), 0, // ESP_PM_TRACE_IDLE + BIT(3), 0, // ESP_PM_TRACE_TICK + BIT(4), 0, // ESP_PM_TRACE_FREQ_SWITCH + BIT(5), 0, // ESP_PM_TRACE_CCOMPARE_UPDATE + BIT(6), 0, // ESP_PM_TRACE_ISR_HOOK + BIT(7), 0, // ESP_PM_TRACE_SLEEP }; void esp_pm_trace_init(void)