pm_impl.c 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. /*
  2. * SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdlib.h>
  7. #include <stdbool.h>
  8. #include <string.h>
  9. #include <stdint.h>
  10. #include <sys/param.h>
  11. #include "esp_attr.h"
  12. #include "esp_err.h"
  13. #include "esp_pm.h"
  14. #include "esp_log.h"
  15. #include "esp_cpu.h"
  16. #include "esp_clk_tree.h"
  17. #include "soc/soc_caps.h"
  18. #include "esp_private/crosscore_int.h"
  19. #include "esp_private/periph_ctrl.h"
  20. #include "soc/rtc.h"
  21. #include "hal/uart_ll.h"
  22. #include "hal/uart_types.h"
  23. #include "driver/gpio.h"
  24. #include "freertos/FreeRTOS.h"
  25. #include "freertos/task.h"
  26. #if CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  27. #include "freertos/xtensa_timer.h"
  28. #include "xtensa/core-macros.h"
  29. #endif
  30. #if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
  31. #include "esp_private/mspi_timing_tuning.h"
  32. #endif
  33. #include "esp_private/pm_impl.h"
  34. #include "esp_private/pm_trace.h"
  35. #include "esp_private/esp_timer_private.h"
  36. #include "esp_private/esp_clk.h"
  37. #include "esp_private/sleep_cpu.h"
  38. #include "esp_private/sleep_gpio.h"
  39. #include "esp_private/sleep_modem.h"
  40. #include "esp_private/uart_share_hw_ctrl.h"
  41. #include "esp_sleep.h"
  42. #include "esp_memory_utils.h"
  43. #include "sdkconfig.h"
  44. #if SOC_PERIPH_CLK_CTRL_SHARED
  45. #define HP_UART_SRC_CLK_ATOMIC() PERIPH_RCC_ATOMIC()
  46. #else
  47. #define HP_UART_SRC_CLK_ATOMIC()
  48. #endif
  49. #define MHZ (1000000)
  50. #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  51. /* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work
  52. * for the purpose of detecting a deadlock.
  53. */
  54. #define CCOMPARE_UPDATE_TIMEOUT 1000000
  55. /* When changing CCOMPARE, don't allow changes if the difference is less
  56. * than this. This is to prevent setting CCOMPARE below CCOUNT.
  57. */
  58. #define CCOMPARE_MIN_CYCLES_IN_FUTURE 1000
  59. #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  60. /* When light sleep is used, wake this number of microseconds earlier than
  61. * the next tick.
  62. */
  63. #define LIGHT_SLEEP_EARLY_WAKEUP_US 100
  64. #if CONFIG_IDF_TARGET_ESP32
  65. /* Minimal divider at which REF_CLK_FREQ can be obtained */
  66. #define REF_CLK_DIV_MIN 10
  67. #elif CONFIG_IDF_TARGET_ESP32S2
  68. /* Minimal divider at which REF_CLK_FREQ can be obtained */
  69. #define REF_CLK_DIV_MIN 2
  70. #elif CONFIG_IDF_TARGET_ESP32S3
  71. /* Minimal divider at which REF_CLK_FREQ can be obtained */
  72. #define REF_CLK_DIV_MIN 2 // TODO: IDF-5660
  73. #elif CONFIG_IDF_TARGET_ESP32C3
  74. #define REF_CLK_DIV_MIN 2
  75. #elif CONFIG_IDF_TARGET_ESP32C2
  76. #define REF_CLK_DIV_MIN 2
  77. #elif CONFIG_IDF_TARGET_ESP32C6
  78. #define REF_CLK_DIV_MIN 2
  79. #elif CONFIG_IDF_TARGET_ESP32H2
  80. #define REF_CLK_DIV_MIN 2
  81. #elif CONFIG_IDF_TARGET_ESP32P4
  82. #define REF_CLK_DIV_MIN 2
  83. #endif
  84. #ifdef CONFIG_PM_PROFILING
  85. #define WITH_PROFILING
  86. #endif
  87. static portMUX_TYPE s_switch_lock = portMUX_INITIALIZER_UNLOCKED;
  88. /* The following state variables are protected using s_switch_lock: */
  89. /* Current sleep mode; When switching, contains old mode until switch is complete */
  90. static pm_mode_t s_mode = PM_MODE_CPU_MAX;
  91. /* True when switch is in progress */
  92. static volatile bool s_is_switching;
  93. /* Number of times each mode was locked */
  94. static size_t s_mode_lock_counts[PM_MODE_COUNT];
  95. /* Bit mask of locked modes. BIT(i) is set iff s_mode_lock_counts[i] > 0. */
  96. static uint32_t s_mode_mask;
  97. #if CONFIG_FREERTOS_USE_TICKLESS_IDLE
  98. #define PERIPH_SKIP_LIGHT_SLEEP_NO 2
  99. /* Indicates if light sleep shoule be skipped by peripherals. */
  100. static skip_light_sleep_cb_t s_periph_skip_light_sleep_cb[PERIPH_SKIP_LIGHT_SLEEP_NO];
  101. /* Indicates if light sleep entry was skipped in vApplicationSleep for given CPU.
  102. * This in turn gets used in IDLE hook to decide if `waiti` needs
  103. * to be invoked or not.
  104. */
  105. static bool s_skipped_light_sleep[portNUM_PROCESSORS];
  106. #if portNUM_PROCESSORS == 2
  107. /* When light sleep is finished on one CPU, it is possible that the other CPU
  108. * will enter light sleep again very soon, before interrupts on the first CPU
  109. * get a chance to run. To avoid such situation, set a flag for the other CPU to
  110. * skip light sleep attempt.
  111. */
  112. static bool s_skip_light_sleep[portNUM_PROCESSORS];
  113. #endif // portNUM_PROCESSORS == 2
  114. static _lock_t s_skip_light_sleep_lock;
  115. #endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
  116. /* A flag indicating that Idle hook has run on a given CPU;
  117. * Next interrupt on the same CPU will take s_rtos_lock_handle.
  118. */
  119. static bool s_core_idle[portNUM_PROCESSORS];
  120. /* When no RTOS tasks are active, these locks are released to allow going into
  121. * a lower power mode. Used by ISR hook and idle hook.
  122. */
  123. static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS];
  124. /* Lookup table of CPU frequency configs to be used in each mode.
  125. * Initialized by esp_pm_impl_init and modified by esp_pm_configure.
  126. */
  127. static rtc_cpu_freq_config_t s_cpu_freq_by_mode[PM_MODE_COUNT];
  128. /* Whether automatic light sleep is enabled */
  129. static bool s_light_sleep_en = false;
  130. /* When configuration is changed, current frequency may not match the
  131. * newly configured frequency for the current mode. This is an indicator
  132. * to the mode switch code to get the actual current frequency instead of
  133. * relying on the current mode.
  134. */
  135. static bool s_config_changed = false;
  136. #ifdef WITH_PROFILING
  137. /* Time, in microseconds, spent so far in each mode */
  138. static pm_time_t s_time_in_mode[PM_MODE_COUNT];
  139. /* Timestamp, in microseconds, when the mode switch last happened */
  140. static pm_time_t s_last_mode_change_time;
  141. /* User-readable mode names, used by esp_pm_impl_dump_stats */
  142. static const char* s_mode_names[] = {
  143. "SLEEP",
  144. "APB_MIN",
  145. "APB_MAX",
  146. "CPU_MAX"
  147. };
  148. static uint32_t s_light_sleep_counts, s_light_sleep_reject_counts;
  149. #endif // WITH_PROFILING
  150. #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  151. /* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU.
  152. * Used in conjunction with cross-core interrupt to update CCOMPARE on the other CPU.
  153. */
  154. static volatile bool s_need_update_ccompare[portNUM_PROCESSORS];
  155. /* Divider and multiplier used to adjust (ccompare - ccount) duration.
  156. * Only set to non-zero values when switch is in progress.
  157. */
  158. static uint32_t s_ccount_div;
  159. static uint32_t s_ccount_mul;
  160. static void update_ccompare(void);
  161. #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  162. static const char* TAG = "pm";
  163. static void do_switch(pm_mode_t new_mode);
  164. static void leave_idle(void);
  165. static void on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us);
  166. pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg)
  167. {
  168. (void) arg;
  169. if (type == ESP_PM_CPU_FREQ_MAX) {
  170. return PM_MODE_CPU_MAX;
  171. } else if (type == ESP_PM_APB_FREQ_MAX) {
  172. return PM_MODE_APB_MAX;
  173. } else if (type == ESP_PM_NO_LIGHT_SLEEP) {
  174. return PM_MODE_APB_MIN;
  175. } else {
  176. // unsupported mode
  177. abort();
  178. }
  179. }
  180. #if CONFIG_PM_LIGHT_SLEEP_CALLBACKS
  181. /**
  182. * @brief Function entry parameter types for light sleep callback functions (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
  183. */
  184. struct _esp_pm_sleep_cb_config_t {
  185. /**
  186. * Callback function defined by user.
  187. */
  188. esp_pm_light_sleep_cb_t cb;
  189. /**
  190. * Input parameters of callback function defined by user.
  191. */
  192. void *arg;
  193. /**
  194. * Execution priority of callback function defined by user.
  195. */
  196. uint32_t prior;
  197. /**
  198. * Next callback function defined by user.
  199. */
  200. struct _esp_pm_sleep_cb_config_t *next;
  201. };
  202. typedef struct _esp_pm_sleep_cb_config_t esp_pm_sleep_cb_config_t;
  203. static esp_pm_sleep_cb_config_t *s_light_sleep_enter_cb_config;
  204. static esp_pm_sleep_cb_config_t *s_light_sleep_exit_cb_config;
  205. static portMUX_TYPE s_sleep_pm_cb_mutex = portMUX_INITIALIZER_UNLOCKED;
  206. esp_err_t esp_pm_light_sleep_register_cbs(esp_pm_sleep_cbs_register_config_t *cbs_conf)
  207. {
  208. if (cbs_conf->enter_cb == NULL && cbs_conf->exit_cb == NULL) {
  209. return ESP_ERR_INVALID_ARG;
  210. }
  211. portENTER_CRITICAL(&s_sleep_pm_cb_mutex);
  212. if (cbs_conf->enter_cb != NULL) {
  213. esp_pm_sleep_cb_config_t **current_enter_ptr = &(s_light_sleep_enter_cb_config);
  214. while (*current_enter_ptr != NULL) {
  215. if (((*current_enter_ptr)->cb) == (cbs_conf->enter_cb)) {
  216. portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
  217. return ESP_FAIL;
  218. }
  219. current_enter_ptr = &((*current_enter_ptr)->next);
  220. }
  221. esp_pm_sleep_cb_config_t *new_enter_config = (esp_pm_sleep_cb_config_t *)heap_caps_malloc(sizeof(esp_pm_sleep_cb_config_t), MALLOC_CAP_INTERNAL);
  222. if (new_enter_config == NULL) {
  223. portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
  224. return ESP_ERR_NO_MEM; /* Memory allocation failed */
  225. }
  226. new_enter_config->cb = cbs_conf->enter_cb;
  227. new_enter_config->arg = cbs_conf->enter_cb_user_arg;
  228. new_enter_config->prior = cbs_conf->enter_cb_prior;
  229. while (*current_enter_ptr != NULL && (*current_enter_ptr)->prior <= new_enter_config->prior) {
  230. current_enter_ptr = &((*current_enter_ptr)->next);
  231. }
  232. new_enter_config->next = *current_enter_ptr;
  233. *current_enter_ptr = new_enter_config;
  234. }
  235. if (cbs_conf->exit_cb != NULL) {
  236. esp_pm_sleep_cb_config_t **current_exit_ptr = &(s_light_sleep_exit_cb_config);
  237. while (*current_exit_ptr != NULL) {
  238. if (((*current_exit_ptr)->cb) == (cbs_conf->exit_cb)) {
  239. portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
  240. return ESP_FAIL;
  241. }
  242. current_exit_ptr = &((*current_exit_ptr)->next);
  243. }
  244. esp_pm_sleep_cb_config_t *new_exit_config = (esp_pm_sleep_cb_config_t *)heap_caps_malloc(sizeof(esp_pm_sleep_cb_config_t), MALLOC_CAP_INTERNAL);
  245. if (new_exit_config == NULL) {
  246. portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
  247. return ESP_ERR_NO_MEM; /* Memory allocation failed */
  248. }
  249. new_exit_config->cb = cbs_conf->exit_cb;
  250. new_exit_config->arg = cbs_conf->exit_cb_user_arg;
  251. new_exit_config->prior = cbs_conf->exit_cb_prior;
  252. while (*current_exit_ptr != NULL && (*current_exit_ptr)->prior <= new_exit_config->prior) {
  253. current_exit_ptr = &((*current_exit_ptr)->next);
  254. }
  255. new_exit_config->next = *current_exit_ptr;
  256. *current_exit_ptr = new_exit_config;
  257. }
  258. portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
  259. return ESP_OK;
  260. }
  261. esp_err_t esp_pm_light_sleep_unregister_cbs(esp_pm_sleep_cbs_register_config_t *cbs_conf)
  262. {
  263. if (cbs_conf->enter_cb == NULL && cbs_conf->exit_cb == NULL) {
  264. return ESP_ERR_INVALID_ARG;
  265. }
  266. portENTER_CRITICAL(&s_sleep_pm_cb_mutex);
  267. if (cbs_conf->enter_cb != NULL) {
  268. esp_pm_sleep_cb_config_t **current_enter_ptr = &(s_light_sleep_enter_cb_config);
  269. while (*current_enter_ptr != NULL) {
  270. if ((*current_enter_ptr)->cb == cbs_conf->enter_cb) {
  271. esp_pm_sleep_cb_config_t *temp = *current_enter_ptr;
  272. *current_enter_ptr = (*current_enter_ptr)->next;
  273. free(temp);
  274. break;
  275. }
  276. current_enter_ptr = &((*current_enter_ptr)->next);
  277. }
  278. }
  279. if (cbs_conf->exit_cb != NULL) {
  280. esp_pm_sleep_cb_config_t **current_exit_ptr = &(s_light_sleep_exit_cb_config);
  281. while (*current_exit_ptr != NULL) {
  282. if ((*current_exit_ptr)->cb == cbs_conf->exit_cb) {
  283. esp_pm_sleep_cb_config_t *temp = *current_exit_ptr;
  284. *current_exit_ptr = (*current_exit_ptr)->next;
  285. free(temp);
  286. break;
  287. }
  288. current_exit_ptr = &((*current_exit_ptr)->next);
  289. }
  290. }
  291. portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
  292. return ESP_OK;
  293. }
  294. static void IRAM_ATTR esp_pm_execute_enter_sleep_callbacks(int64_t sleep_time_us)
  295. {
  296. esp_pm_sleep_cb_config_t *enter_current = s_light_sleep_enter_cb_config;
  297. while (enter_current != NULL) {
  298. if (enter_current->cb != NULL) {
  299. if (ESP_OK != (*enter_current->cb)(sleep_time_us, enter_current->arg)) {
  300. ESP_EARLY_LOGW(TAG, "esp_pm_execute_enter_sleep_callbacks has an err, enter_current = %p", enter_current);
  301. }
  302. }
  303. enter_current = enter_current->next;
  304. }
  305. }
  306. static void IRAM_ATTR esp_pm_execute_exit_sleep_callbacks(int64_t sleep_time_us)
  307. {
  308. esp_pm_sleep_cb_config_t *exit_current = s_light_sleep_exit_cb_config;
  309. while (exit_current != NULL) {
  310. if (exit_current->cb != NULL) {
  311. if (ESP_OK != (*exit_current->cb)(sleep_time_us, exit_current->arg)) {
  312. ESP_EARLY_LOGW(TAG, "esp_pm_execute_exit_sleep_callbacks has an err, exit_current = %p", exit_current);
  313. }
  314. }
  315. exit_current = exit_current->next;
  316. }
  317. }
  318. #endif
  319. static esp_err_t esp_pm_sleep_configure(const void *vconfig)
  320. {
  321. esp_err_t err = ESP_OK;
  322. const esp_pm_config_t* config = (const esp_pm_config_t*) vconfig;
  323. #if SOC_PM_SUPPORT_CPU_PD
  324. err = sleep_cpu_configure(config->light_sleep_enable);
  325. if (err != ESP_OK) {
  326. return err;
  327. }
  328. #endif
  329. err = sleep_modem_configure(config->max_freq_mhz, config->min_freq_mhz, config->light_sleep_enable);
  330. return err;
  331. }
  332. esp_err_t esp_pm_configure(const void* vconfig)
  333. {
  334. #ifndef CONFIG_PM_ENABLE
  335. return ESP_ERR_NOT_SUPPORTED;
  336. #endif
  337. const esp_pm_config_t* config = (const esp_pm_config_t*) vconfig;
  338. #ifndef CONFIG_FREERTOS_USE_TICKLESS_IDLE
  339. if (config->light_sleep_enable) {
  340. return ESP_ERR_NOT_SUPPORTED;
  341. }
  342. #endif
  343. int min_freq_mhz = config->min_freq_mhz;
  344. int max_freq_mhz = config->max_freq_mhz;
  345. if (min_freq_mhz > max_freq_mhz) {
  346. return ESP_ERR_INVALID_ARG;
  347. }
  348. rtc_cpu_freq_config_t freq_config;
  349. if (!rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &freq_config)) {
  350. ESP_LOGW(TAG, "invalid min_freq_mhz value (%d)", min_freq_mhz);
  351. return ESP_ERR_INVALID_ARG;
  352. }
  353. int xtal_freq_mhz = esp_clk_xtal_freq() / MHZ;
  354. if (min_freq_mhz < xtal_freq_mhz && min_freq_mhz * MHZ / REF_CLK_FREQ < REF_CLK_DIV_MIN) {
  355. ESP_LOGW(TAG, "min_freq_mhz should be >= %d", REF_CLK_FREQ * REF_CLK_DIV_MIN / MHZ);
  356. return ESP_ERR_INVALID_ARG;
  357. }
  358. if (!rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &freq_config)) {
  359. ESP_LOGW(TAG, "invalid max_freq_mhz value (%d)", max_freq_mhz);
  360. return ESP_ERR_INVALID_ARG;
  361. }
  362. #if CONFIG_IDF_TARGET_ESP32
  363. int apb_max_freq = max_freq_mhz; /* CPU frequency in APB_MAX mode */
  364. if (max_freq_mhz == 240) {
  365. /* We can't switch between 240 and 80/160 without disabling PLL,
  366. * so use 240MHz CPU frequency when 80MHz APB frequency is requested.
  367. */
  368. apb_max_freq = 240;
  369. } else if (max_freq_mhz == 160 || max_freq_mhz == 80) {
  370. /* Otherwise, can use 80MHz
  371. * CPU frequency when 80MHz APB frequency is requested.
  372. */
  373. apb_max_freq = 80;
  374. }
  375. #else
  376. /* Maximum SOC APB clock frequency is 40 MHz, maximum Modem (WiFi,
  377. * Bluetooth, etc..) APB clock frequency is 80 MHz */
  378. int apb_clk_freq = esp_clk_apb_freq() / MHZ;
  379. #if CONFIG_ESP_WIFI_ENABLED || CONFIG_BT_ENABLED || CONFIG_IEEE802154_ENABLED
  380. apb_clk_freq = MAX(apb_clk_freq, MODEM_REQUIRED_MIN_APB_CLK_FREQ / MHZ);
  381. #endif
  382. int apb_max_freq = MIN(max_freq_mhz, apb_clk_freq); /* CPU frequency in APB_MAX mode */
  383. #endif
  384. apb_max_freq = MAX(apb_max_freq, min_freq_mhz);
  385. ESP_LOGI(TAG, "Frequency switching config: "
  386. "CPU_MAX: %d, APB_MAX: %d, APB_MIN: %d, Light sleep: %s",
  387. max_freq_mhz,
  388. apb_max_freq,
  389. min_freq_mhz,
  390. config->light_sleep_enable ? "ENABLED" : "DISABLED");
  391. // CPU & Modem power down initialization, which must be initialized before s_light_sleep_en set true,
  392. // to avoid entering idle and sleep in this function.
  393. esp_pm_sleep_configure(config);
  394. portENTER_CRITICAL(&s_switch_lock);
  395. bool res __attribute__((unused));
  396. res = rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_CPU_MAX]);
  397. assert(res);
  398. res = rtc_clk_cpu_freq_mhz_to_config(apb_max_freq, &s_cpu_freq_by_mode[PM_MODE_APB_MAX]);
  399. assert(res);
  400. res = rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_APB_MIN]);
  401. assert(res);
  402. s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = s_cpu_freq_by_mode[PM_MODE_APB_MIN];
  403. s_light_sleep_en = config->light_sleep_enable;
  404. s_config_changed = true;
  405. portEXIT_CRITICAL(&s_switch_lock);
  406. return ESP_OK;
  407. }
  408. esp_err_t esp_pm_get_configuration(void* vconfig)
  409. {
  410. if (vconfig == NULL) {
  411. return ESP_ERR_INVALID_ARG;
  412. }
  413. esp_pm_config_t* config = (esp_pm_config_t*) vconfig;
  414. portENTER_CRITICAL(&s_switch_lock);
  415. config->light_sleep_enable = s_light_sleep_en;
  416. config->max_freq_mhz = s_cpu_freq_by_mode[PM_MODE_CPU_MAX].freq_mhz;
  417. config->min_freq_mhz = s_cpu_freq_by_mode[PM_MODE_APB_MIN].freq_mhz;
  418. portEXIT_CRITICAL(&s_switch_lock);
  419. return ESP_OK;
  420. }
  421. static pm_mode_t IRAM_ATTR get_lowest_allowed_mode(void)
  422. {
  423. /* TODO: optimize using ffs/clz */
  424. if (s_mode_mask >= BIT(PM_MODE_CPU_MAX)) {
  425. return PM_MODE_CPU_MAX;
  426. } else if (s_mode_mask >= BIT(PM_MODE_APB_MAX)) {
  427. return PM_MODE_APB_MAX;
  428. } else if (s_mode_mask >= BIT(PM_MODE_APB_MIN) || !s_light_sleep_en) {
  429. return PM_MODE_APB_MIN;
  430. } else {
  431. return PM_MODE_LIGHT_SLEEP;
  432. }
  433. }
  434. void IRAM_ATTR esp_pm_impl_switch_mode(pm_mode_t mode,
  435. pm_mode_switch_t lock_or_unlock, pm_time_t now)
  436. {
  437. bool need_switch = false;
  438. uint32_t mode_mask = BIT(mode);
  439. portENTER_CRITICAL_SAFE(&s_switch_lock);
  440. uint32_t count;
  441. if (lock_or_unlock == MODE_LOCK) {
  442. count = ++s_mode_lock_counts[mode];
  443. } else {
  444. count = s_mode_lock_counts[mode]--;
  445. }
  446. if (count == 1) {
  447. if (lock_or_unlock == MODE_LOCK) {
  448. s_mode_mask |= mode_mask;
  449. } else {
  450. s_mode_mask &= ~mode_mask;
  451. }
  452. need_switch = true;
  453. }
  454. pm_mode_t new_mode = s_mode;
  455. if (need_switch) {
  456. new_mode = get_lowest_allowed_mode();
  457. #ifdef WITH_PROFILING
  458. if (s_last_mode_change_time != 0) {
  459. pm_time_t diff = now - s_last_mode_change_time;
  460. s_time_in_mode[s_mode] += diff;
  461. }
  462. s_last_mode_change_time = now;
  463. #endif // WITH_PROFILING
  464. }
  465. portEXIT_CRITICAL_SAFE(&s_switch_lock);
  466. if (need_switch) {
  467. do_switch(new_mode);
  468. }
  469. }
  470. /**
  471. * @brief Update clock dividers in esp_timer and FreeRTOS, and adjust CCOMPARE
  472. * values on both CPUs.
  473. * @param old_ticks_per_us old CPU frequency
  474. * @param ticks_per_us new CPU frequency
  475. */
  476. static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us)
  477. {
  478. uint32_t old_apb_ticks_per_us = MIN(old_ticks_per_us, 80);
  479. uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80);
  480. /* Update APB frequency value used by the timer */
  481. if (old_apb_ticks_per_us != apb_ticks_per_us) {
  482. esp_timer_private_update_apb_freq(apb_ticks_per_us);
  483. }
  484. #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  485. #ifdef XT_RTOS_TIMER_INT
  486. /* Calculate new tick divisor */
  487. _xt_tick_divisor = ticks_per_us * MHZ / XT_TICK_PER_SEC;
  488. #endif
  489. int core_id = xPortGetCoreID();
  490. if (s_rtos_lock_handle[core_id] != NULL) {
  491. ESP_PM_TRACE_ENTER(CCOMPARE_UPDATE, core_id);
  492. /* ccount_div and ccount_mul are used in esp_pm_impl_update_ccompare
  493. * to calculate new CCOMPARE value.
  494. */
  495. s_ccount_div = old_ticks_per_us;
  496. s_ccount_mul = ticks_per_us;
  497. /* Update CCOMPARE value on this CPU */
  498. update_ccompare();
  499. #if portNUM_PROCESSORS == 2
  500. /* Send interrupt to the other CPU to update CCOMPARE value */
  501. int other_core_id = (core_id == 0) ? 1 : 0;
  502. s_need_update_ccompare[other_core_id] = true;
  503. esp_crosscore_int_send_freq_switch(other_core_id);
  504. int timeout = 0;
  505. while (s_need_update_ccompare[other_core_id]) {
  506. if (++timeout == CCOMPARE_UPDATE_TIMEOUT) {
  507. assert(false && "failed to update CCOMPARE, possible deadlock");
  508. }
  509. }
  510. #endif // portNUM_PROCESSORS == 2
  511. s_ccount_mul = 0;
  512. s_ccount_div = 0;
  513. ESP_PM_TRACE_EXIT(CCOMPARE_UPDATE, core_id);
  514. }
  515. #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  516. }
  517. /**
  518. * Perform the switch to new power mode.
  519. * Currently only changes the CPU frequency and adjusts clock dividers.
  520. * No light sleep yet.
  521. * @param new_mode mode to switch to
  522. */
  523. static void IRAM_ATTR do_switch(pm_mode_t new_mode)
  524. {
  525. const int core_id = xPortGetCoreID();
  526. do {
  527. portENTER_CRITICAL_ISR(&s_switch_lock);
  528. if (!s_is_switching) {
  529. break;
  530. }
  531. #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  532. if (s_need_update_ccompare[core_id]) {
  533. s_need_update_ccompare[core_id] = false;
  534. }
  535. #endif
  536. portEXIT_CRITICAL_ISR(&s_switch_lock);
  537. } while (true);
  538. if (new_mode == s_mode) {
  539. portEXIT_CRITICAL_ISR(&s_switch_lock);
  540. return;
  541. }
  542. s_is_switching = true;
  543. bool config_changed = s_config_changed;
  544. s_config_changed = false;
  545. portEXIT_CRITICAL_ISR(&s_switch_lock);
  546. rtc_cpu_freq_config_t new_config = s_cpu_freq_by_mode[new_mode];
  547. rtc_cpu_freq_config_t old_config;
  548. if (!config_changed) {
  549. old_config = s_cpu_freq_by_mode[s_mode];
  550. } else {
  551. rtc_clk_cpu_freq_get_config(&old_config);
  552. }
  553. if (new_config.freq_mhz != old_config.freq_mhz) {
  554. uint32_t old_ticks_per_us = old_config.freq_mhz;
  555. uint32_t new_ticks_per_us = new_config.freq_mhz;
  556. bool switch_down = new_ticks_per_us < old_ticks_per_us;
  557. ESP_PM_TRACE_ENTER(FREQ_SWITCH, core_id);
  558. if (switch_down) {
  559. on_freq_update(old_ticks_per_us, new_ticks_per_us);
  560. }
  561. if (new_config.source == SOC_CPU_CLK_SRC_PLL) {
  562. rtc_clk_cpu_freq_set_config_fast(&new_config);
  563. #if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
  564. mspi_timing_change_speed_mode_cache_safe(false);
  565. #endif
  566. } else {
  567. #if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
  568. mspi_timing_change_speed_mode_cache_safe(true);
  569. #endif
  570. rtc_clk_cpu_freq_set_config_fast(&new_config);
  571. }
  572. if (!switch_down) {
  573. on_freq_update(old_ticks_per_us, new_ticks_per_us);
  574. }
  575. ESP_PM_TRACE_EXIT(FREQ_SWITCH, core_id);
  576. }
  577. portENTER_CRITICAL_ISR(&s_switch_lock);
  578. s_mode = new_mode;
  579. s_is_switching = false;
  580. portEXIT_CRITICAL_ISR(&s_switch_lock);
  581. }
  582. #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  583. /**
  584. * @brief Calculate new CCOMPARE value based on s_ccount_{mul,div}
  585. *
  586. * Adjusts CCOMPARE value so that the interrupt happens at the same time as it
  587. * would happen without the frequency change.
  588. * Assumes that the new_frequency = old_frequency * s_ccount_mul / s_ccount_div.
  589. */
  590. static void IRAM_ATTR update_ccompare(void)
  591. {
  592. #if CONFIG_PM_UPDATE_CCOMPARE_HLI_WORKAROUND
  593. /* disable level 4 and below */
  594. uint32_t irq_status = XTOS_SET_INTLEVEL(XCHAL_DEBUGLEVEL - 2);
  595. #endif
  596. uint32_t ccount = esp_cpu_get_cycle_count();
  597. uint32_t ccompare = XTHAL_GET_CCOMPARE(XT_TIMER_INDEX);
  598. if ((ccompare - CCOMPARE_MIN_CYCLES_IN_FUTURE) - ccount < UINT32_MAX / 2) {
  599. uint32_t diff = ccompare - ccount;
  600. uint32_t diff_scaled = (diff * s_ccount_mul + s_ccount_div - 1) / s_ccount_div;
  601. if (diff_scaled < _xt_tick_divisor) {
  602. uint32_t new_ccompare = ccount + diff_scaled;
  603. XTHAL_SET_CCOMPARE(XT_TIMER_INDEX, new_ccompare);
  604. }
  605. }
  606. #if CONFIG_PM_UPDATE_CCOMPARE_HLI_WORKAROUND
  607. XTOS_RESTORE_INTLEVEL(irq_status);
  608. #endif
  609. }
  610. #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  611. static void IRAM_ATTR leave_idle(void)
  612. {
  613. int core_id = xPortGetCoreID();
  614. if (s_core_idle[core_id]) {
  615. // TODO: possible optimization: raise frequency here first
  616. esp_pm_lock_acquire(s_rtos_lock_handle[core_id]);
  617. s_core_idle[core_id] = false;
  618. }
  619. }
  620. #if CONFIG_FREERTOS_USE_TICKLESS_IDLE
  621. esp_err_t esp_pm_register_skip_light_sleep_callback(skip_light_sleep_cb_t cb)
  622. {
  623. _lock_acquire(&s_skip_light_sleep_lock);
  624. for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) {
  625. if (s_periph_skip_light_sleep_cb[i] == cb) {
  626. _lock_release(&s_skip_light_sleep_lock);
  627. return ESP_OK;
  628. } else if (s_periph_skip_light_sleep_cb[i] == NULL) {
  629. s_periph_skip_light_sleep_cb[i] = cb;
  630. _lock_release(&s_skip_light_sleep_lock);
  631. return ESP_OK;
  632. }
  633. }
  634. _lock_release(&s_skip_light_sleep_lock);
  635. return ESP_ERR_NO_MEM;
  636. }
  637. esp_err_t esp_pm_unregister_skip_light_sleep_callback(skip_light_sleep_cb_t cb)
  638. {
  639. _lock_acquire(&s_skip_light_sleep_lock);
  640. for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) {
  641. if (s_periph_skip_light_sleep_cb[i] == cb) {
  642. s_periph_skip_light_sleep_cb[i] = NULL;
  643. _lock_release(&s_skip_light_sleep_lock);
  644. return ESP_OK;
  645. }
  646. }
  647. _lock_release(&s_skip_light_sleep_lock);
  648. return ESP_ERR_INVALID_STATE;
  649. }
  650. static inline bool IRAM_ATTR periph_should_skip_light_sleep(void)
  651. {
  652. if (s_light_sleep_en) {
  653. for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) {
  654. if (s_periph_skip_light_sleep_cb[i]) {
  655. if (s_periph_skip_light_sleep_cb[i]() == true) {
  656. return true;
  657. }
  658. }
  659. }
  660. }
  661. return false;
  662. }
  663. static inline bool IRAM_ATTR should_skip_light_sleep(int core_id)
  664. {
  665. #if portNUM_PROCESSORS == 2
  666. if (s_skip_light_sleep[core_id]) {
  667. s_skip_light_sleep[core_id] = false;
  668. s_skipped_light_sleep[core_id] = true;
  669. return true;
  670. }
  671. #endif // portNUM_PROCESSORS == 2
  672. if (s_mode != PM_MODE_LIGHT_SLEEP || s_is_switching || periph_should_skip_light_sleep()) {
  673. s_skipped_light_sleep[core_id] = true;
  674. } else {
  675. s_skipped_light_sleep[core_id] = false;
  676. }
  677. return s_skipped_light_sleep[core_id];
  678. }
  679. static inline void IRAM_ATTR other_core_should_skip_light_sleep(int core_id)
  680. {
  681. #if portNUM_PROCESSORS == 2
  682. s_skip_light_sleep[!core_id] = true;
  683. #endif
  684. }
  685. void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
  686. {
  687. portENTER_CRITICAL(&s_switch_lock);
  688. int core_id = xPortGetCoreID();
  689. if (!should_skip_light_sleep(core_id)) {
  690. /* Calculate how much we can sleep */
  691. int64_t next_esp_timer_alarm = esp_timer_get_next_alarm_for_wake_up();
  692. int64_t now = esp_timer_get_time();
  693. int64_t time_until_next_alarm = next_esp_timer_alarm - now;
  694. int64_t wakeup_delay_us = portTICK_PERIOD_MS * 1000LL * xExpectedIdleTime;
  695. int64_t sleep_time_us = MIN(wakeup_delay_us, time_until_next_alarm);
  696. int64_t slept_us = 0;
  697. #if CONFIG_PM_LIGHT_SLEEP_CALLBACKS
  698. uint32_t cycle = esp_cpu_get_cycle_count();
  699. esp_pm_execute_enter_sleep_callbacks(sleep_time_us);
  700. sleep_time_us -= (esp_cpu_get_cycle_count() - cycle) / (esp_clk_cpu_freq() / 1000000ULL);
  701. #endif
  702. if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL) {
  703. esp_sleep_enable_timer_wakeup(sleep_time_us - LIGHT_SLEEP_EARLY_WAKEUP_US);
  704. #if CONFIG_PM_TRACE && SOC_PM_SUPPORT_RTC_PERIPH_PD
  705. /* to force tracing GPIOs to keep state */
  706. esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
  707. #endif
  708. /* Enter sleep */
  709. ESP_PM_TRACE_ENTER(SLEEP, core_id);
  710. int64_t sleep_start = esp_timer_get_time();
  711. if (esp_light_sleep_start() != ESP_OK){
  712. #ifdef WITH_PROFILING
  713. s_light_sleep_reject_counts++;
  714. } else {
  715. s_light_sleep_counts++;
  716. #endif
  717. }
  718. slept_us = esp_timer_get_time() - sleep_start;
  719. ESP_PM_TRACE_EXIT(SLEEP, core_id);
  720. uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL);
  721. if (slept_ticks > 0) {
  722. /* Adjust RTOS tick count based on the amount of time spent in sleep */
  723. vTaskStepTick(slept_ticks);
  724. #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
  725. /* Trigger tick interrupt, since sleep time was longer
  726. * than portTICK_PERIOD_MS. Note that setting INTSET does not
  727. * work for timer interrupt, and changing CCOMPARE would clear
  728. * the interrupt flag.
  729. */
  730. esp_cpu_set_cycle_count(XTHAL_GET_CCOMPARE(XT_TIMER_INDEX) - 16);
  731. while (!(XTHAL_GET_INTERRUPT() & BIT(XT_TIMER_INTNUM))) {
  732. ;
  733. }
  734. #else
  735. portYIELD_WITHIN_API();
  736. #endif
  737. }
  738. other_core_should_skip_light_sleep(core_id);
  739. }
  740. #if CONFIG_PM_LIGHT_SLEEP_CALLBACKS
  741. esp_pm_execute_exit_sleep_callbacks(slept_us);
  742. #endif
  743. }
  744. portEXIT_CRITICAL(&s_switch_lock);
  745. }
  746. #endif //CONFIG_FREERTOS_USE_TICKLESS_IDLE
  747. #ifdef WITH_PROFILING
  748. void esp_pm_impl_dump_stats(FILE* out)
  749. {
  750. pm_time_t time_in_mode[PM_MODE_COUNT];
  751. portENTER_CRITICAL_ISR(&s_switch_lock);
  752. memcpy(time_in_mode, s_time_in_mode, sizeof(time_in_mode));
  753. pm_time_t last_mode_change_time = s_last_mode_change_time;
  754. pm_mode_t cur_mode = s_mode;
  755. pm_time_t now = pm_get_time();
  756. bool light_sleep_en = s_light_sleep_en;
  757. uint32_t light_sleep_counts = s_light_sleep_counts;
  758. uint32_t light_sleep_reject_counts = s_light_sleep_reject_counts;
  759. portEXIT_CRITICAL_ISR(&s_switch_lock);
  760. time_in_mode[cur_mode] += now - last_mode_change_time;
  761. fprintf(out, "\nMode stats:\n");
  762. fprintf(out, "%-8s %-10s %-10s %-10s\n", "Mode", "CPU_freq", "Time(us)", "Time(%)");
  763. for (int i = 0; i < PM_MODE_COUNT; ++i) {
  764. if (i == PM_MODE_LIGHT_SLEEP && !light_sleep_en) {
  765. /* don't display light sleep mode if it's not enabled */
  766. continue;
  767. }
  768. fprintf(out, "%-8s %-3"PRIu32"M%-7s %-10lld %-2d%%\n",
  769. s_mode_names[i],
  770. s_cpu_freq_by_mode[i].freq_mhz,
  771. "", //Empty space to align columns
  772. time_in_mode[i],
  773. (int) (time_in_mode[i] * 100 / now));
  774. }
  775. if (light_sleep_en){
  776. fprintf(out, "\nSleep stats:\n");
  777. fprintf(out, "light_sleep_counts:%ld light_sleep_reject_counts:%ld\n", light_sleep_counts, light_sleep_reject_counts);
  778. }
  779. }
  780. #endif // WITH_PROFILING
  781. int esp_pm_impl_get_cpu_freq(pm_mode_t mode)
  782. {
  783. int freq_mhz;
  784. if (mode >= PM_MODE_LIGHT_SLEEP && mode < PM_MODE_COUNT) {
  785. portENTER_CRITICAL(&s_switch_lock);
  786. freq_mhz = s_cpu_freq_by_mode[mode].freq_mhz;
  787. portEXIT_CRITICAL(&s_switch_lock);
  788. } else {
  789. abort();
  790. }
  791. return freq_mhz;
  792. }
  793. void esp_pm_impl_init(void)
  794. {
  795. #if defined(CONFIG_ESP_CONSOLE_UART)
  796. //This clock source should be a source which won't be affected by DFS
  797. uart_sclk_t clk_source = UART_SCLK_DEFAULT;
  798. #if SOC_UART_SUPPORT_REF_TICK
  799. clk_source = UART_SCLK_REF_TICK;
  800. #elif SOC_UART_SUPPORT_XTAL_CLK
  801. clk_source = UART_SCLK_XTAL;
  802. #else
  803. #error "No UART clock source is aware of DFS"
  804. #endif // SOC_UART_SUPPORT_xxx
  805. while (!uart_ll_is_tx_idle(UART_LL_GET_HW(CONFIG_ESP_CONSOLE_UART_NUM))) {
  806. ;
  807. }
  808. /* When DFS is enabled, override system setting and use REFTICK as UART clock source */
  809. HP_UART_SRC_CLK_ATOMIC() {
  810. uart_ll_set_sclk(UART_LL_GET_HW(CONFIG_ESP_CONSOLE_UART_NUM), (soc_module_clk_t)clk_source);
  811. }
  812. uint32_t sclk_freq;
  813. esp_err_t err = esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_source, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &sclk_freq);
  814. assert(err == ESP_OK);
  815. HP_UART_SRC_CLK_ATOMIC() {
  816. uart_ll_set_baudrate(UART_LL_GET_HW(CONFIG_ESP_CONSOLE_UART_NUM), CONFIG_ESP_CONSOLE_UART_BAUDRATE, sclk_freq);
  817. }
  818. #endif // CONFIG_ESP_CONSOLE_UART
  819. #ifdef CONFIG_PM_TRACE
  820. esp_pm_trace_init();
  821. #endif
  822. ESP_ERROR_CHECK(esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "rtos0",
  823. &s_rtos_lock_handle[0]));
  824. ESP_ERROR_CHECK(esp_pm_lock_acquire(s_rtos_lock_handle[0]));
  825. #if portNUM_PROCESSORS == 2
  826. ESP_ERROR_CHECK(esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "rtos1",
  827. &s_rtos_lock_handle[1]));
  828. ESP_ERROR_CHECK(esp_pm_lock_acquire(s_rtos_lock_handle[1]));
  829. #endif // portNUM_PROCESSORS == 2
  830. /* Configure all modes to use the default CPU frequency.
  831. * This will be modified later by a call to esp_pm_configure.
  832. */
  833. rtc_cpu_freq_config_t default_config;
  834. if (!rtc_clk_cpu_freq_mhz_to_config(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, &default_config)) {
  835. assert(false && "unsupported frequency");
  836. }
  837. for (size_t i = 0; i < PM_MODE_COUNT; ++i) {
  838. s_cpu_freq_by_mode[i] = default_config;
  839. }
  840. #ifdef CONFIG_PM_DFS_INIT_AUTO
  841. int xtal_freq_mhz = esp_clk_xtal_freq() / MHZ;
  842. esp_pm_config_t cfg = {
  843. .max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
  844. .min_freq_mhz = xtal_freq_mhz,
  845. };
  846. esp_pm_configure(&cfg);
  847. #endif //CONFIG_PM_DFS_INIT_AUTO
  848. }
  849. void esp_pm_impl_idle_hook(void)
  850. {
  851. int core_id = xPortGetCoreID();
  852. #if CONFIG_FREERTOS_SMP
  853. uint32_t state = portDISABLE_INTERRUPTS();
  854. #else
  855. uint32_t state = portSET_INTERRUPT_MASK_FROM_ISR();
  856. #endif
  857. if (!s_core_idle[core_id]
  858. #ifdef CONFIG_FREERTOS_USE_TICKLESS_IDLE
  859. && !periph_should_skip_light_sleep()
  860. #endif
  861. ) {
  862. esp_pm_lock_release(s_rtos_lock_handle[core_id]);
  863. s_core_idle[core_id] = true;
  864. }
  865. #if CONFIG_FREERTOS_SMP
  866. portRESTORE_INTERRUPTS(state);
  867. #else
  868. portCLEAR_INTERRUPT_MASK_FROM_ISR(state);
  869. #endif
  870. ESP_PM_TRACE_ENTER(IDLE, core_id);
  871. }
  872. void IRAM_ATTR esp_pm_impl_isr_hook(void)
  873. {
  874. int core_id = xPortGetCoreID();
  875. ESP_PM_TRACE_ENTER(ISR_HOOK, core_id);
  876. /* Prevent higher level interrupts (than the one this function was called from)
  877. * from happening in this section, since they will also call into esp_pm_impl_isr_hook.
  878. */
  879. #if CONFIG_FREERTOS_SMP
  880. uint32_t state = portDISABLE_INTERRUPTS();
  881. #else
  882. uint32_t state = portSET_INTERRUPT_MASK_FROM_ISR();
  883. #endif
  884. #if defined(CONFIG_FREERTOS_SYSTICK_USES_CCOUNT) && (portNUM_PROCESSORS == 2)
  885. if (s_need_update_ccompare[core_id]) {
  886. update_ccompare();
  887. s_need_update_ccompare[core_id] = false;
  888. } else {
  889. leave_idle();
  890. }
  891. #else
  892. leave_idle();
  893. #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT && portNUM_PROCESSORS == 2
  894. #if CONFIG_FREERTOS_SMP
  895. portRESTORE_INTERRUPTS(state);
  896. #else
  897. portCLEAR_INTERRUPT_MASK_FROM_ISR(state);
  898. #endif
  899. ESP_PM_TRACE_EXIT(ISR_HOOK, core_id);
  900. }
  901. void esp_pm_impl_waiti(void)
  902. {
  903. #if CONFIG_FREERTOS_USE_TICKLESS_IDLE
  904. int core_id = xPortGetCoreID();
  905. if (s_skipped_light_sleep[core_id]) {
  906. esp_cpu_wait_for_intr();
  907. /* Interrupt took the CPU out of waiti and s_rtos_lock_handle[core_id]
  908. * is now taken. However since we are back to idle task, we can release
  909. * the lock so that vApplicationSleep can attempt to enter light sleep.
  910. */
  911. esp_pm_impl_idle_hook();
  912. }
  913. s_skipped_light_sleep[core_id] = true;
  914. #else
  915. esp_cpu_wait_for_intr();
  916. #endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
  917. }