nrf_pwr_mgmt.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /**
  2. * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification,
  7. * are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form, except as embedded into a Nordic
  13. * Semiconductor ASA integrated circuit in a product or a software update for
  14. * such product, must reproduce the above copyright notice, this list of
  15. * conditions and the following disclaimer in the documentation and/or other
  16. * materials provided with the distribution.
  17. *
  18. * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * 4. This software, with or without modification, must only be used with a
  23. * Nordic Semiconductor ASA integrated circuit.
  24. *
  25. * 5. Any software provided in binary form under this license must not be reverse
  26. * engineered, decompiled, modified and/or disassembled.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
  29. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  30. * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
  32. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  33. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. */
  40. #include "sdk_common.h"
  41. #if NRF_MODULE_ENABLED(NRF_PWR_MGMT)
  42. #include "nrf_pwr_mgmt.h"
  43. #include "nrf.h"
  44. #include "nrf_mtx.h"
  45. #include "nrf_power.h"
  46. #include "app_error.h"
  47. #include "nrf_assert.h"
  48. #include "nrf_log_ctrl.h"
  49. #include "app_util_platform.h"
  50. #define NRF_LOG_MODULE_NAME pwr_mgmt
  51. #if NRF_PWR_MGMT_CONFIG_LOG_ENABLED
  52. #define NRF_LOG_LEVEL NRF_PWR_MGMT_CONFIG_LOG_LEVEL
  53. #define NRF_LOG_INFO_COLOR NRF_PWR_MGMT_CONFIG_INFO_COLOR
  54. #define NRF_LOG_DEBUG_COLOR NRF_PWR_MGMT_CONFIG_DEBUG_COLOR
  55. #else
  56. #define NRF_LOG_LEVEL 0
  57. #endif // NRF_PWR_MGMT_CONFIG_LOG_ENABLED
  58. #include "nrf_log.h"
  59. NRF_LOG_MODULE_REGISTER();
  60. #ifdef SOFTDEVICE_PRESENT
  61. #include "nrf_soc.h"
  62. #include "nrf_sdh.h"
  63. #endif // SOFTDEVICE_PRESENT
  64. #if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
  65. #if (APP_SCHEDULER_ENABLED != 1)
  66. #error "APP_SCHEDULER is required."
  67. #endif
  68. #include "app_scheduler.h"
  69. #endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
  70. // Create section "pwr_mgmt_data".
  71. NRF_SECTION_SET_DEF(pwr_mgmt_data,
  72. nrf_pwr_mgmt_shutdown_handler_t,
  73. NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT);
  74. static nrf_pwr_mgmt_evt_t m_pwr_mgmt_evt; /**< Event type which will be passed to the shutdown
  75. handlers.*/
  76. static nrf_mtx_t m_sysoff_mtx; /**< Module API lock.*/
  77. static bool m_shutdown_started; /**< True if application started the shutdown preparation. */
  78. static nrf_section_iter_t m_handlers_iter; /**< Shutdown handlers iterator. */
  79. #if (NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED && __FPU_PRESENT)
  80. #define PWR_MGMT_FPU_SLEEP_PREPARE() pwr_mgmt_fpu_sleep_prepare()
  81. __STATIC_INLINE void pwr_mgmt_fpu_sleep_prepare(void)
  82. {
  83. uint32_t original_fpscr;
  84. CRITICAL_REGION_ENTER();
  85. original_fpscr = __get_FPSCR();
  86. /*
  87. * Clear FPU exceptions.
  88. * Without this step, the FPU interrupt is marked as pending,
  89. * preventing system from sleeping. Exceptions cleared:
  90. * - IOC - Invalid Operation cumulative exception bit.
  91. * - DZC - Division by Zero cumulative exception bit.
  92. * - OFC - Overflow cumulative exception bit.
  93. * - UFC - Underflow cumulative exception bit.
  94. * - IXC - Inexact cumulative exception bit.
  95. * - IDC - Input Denormal cumulative exception bit.
  96. */
  97. __set_FPSCR(original_fpscr & ~0x9Fu);
  98. __DMB();
  99. NVIC_ClearPendingIRQ(FPU_IRQn);
  100. CRITICAL_REGION_EXIT();
  101. /*
  102. * The last chance to indicate an error in FPU to the user
  103. * as the FPSCR is now cleared
  104. *
  105. * This assert is related to previous FPU operations
  106. * and not power management.
  107. *
  108. * Critical FPU exceptions signaled:
  109. * - IOC - Invalid Operation cumulative exception bit.
  110. * - DZC - Division by Zero cumulative exception bit.
  111. * - OFC - Overflow cumulative exception bit.
  112. */
  113. ASSERT((original_fpscr & 0x7) == 0);
  114. }
  115. #else
  116. #define PWR_MGMT_FPU_SLEEP_PREPARE()
  117. #endif // NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED
  118. #if NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED
  119. #undef PWR_MGMT_SLEEP_IN_CRITICAL_SECTION_REQUIRED
  120. #define PWR_MGMT_SLEEP_IN_CRITICAL_SECTION_REQUIRED
  121. #include "nrf_gpio.h"
  122. #define PWR_MGMT_DEBUG_PINS_INIT() pwr_mgmt_debug_pins_init()
  123. #define PWR_MGMT_DEBUG_PIN_CLEAR() nrf_gpio_pin_clear(NRF_PWR_MGMT_SLEEP_DEBUG_PIN)
  124. #define PWR_MGMT_DEBUG_PIN_SET() nrf_gpio_pin_set(NRF_PWR_MGMT_SLEEP_DEBUG_PIN)
  125. __STATIC_INLINE void pwr_mgmt_debug_pins_init(void)
  126. {
  127. nrf_gpio_pin_clear(NRF_PWR_MGMT_SLEEP_DEBUG_PIN);
  128. nrf_gpio_cfg_output(NRF_PWR_MGMT_SLEEP_DEBUG_PIN);
  129. }
  130. #else
  131. #define PWR_MGMT_DEBUG_PIN_CLEAR()
  132. #define PWR_MGMT_DEBUG_PIN_SET()
  133. #define PWR_MGMT_DEBUG_PINS_INIT()
  134. #endif
  135. #if NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED
  136. #undef PWR_MGMT_SLEEP_IN_CRITICAL_SECTION_REQUIRED
  137. #define PWR_MGMT_SLEEP_IN_CRITICAL_SECTION_REQUIRED
  138. #undef PWR_MGMT_TIMER_REQUIRED
  139. #define PWR_MGMT_TIMER_REQUIRED
  140. #include "app_timer.h"
  141. #define PWR_MGMT_CPU_USAGE_MONITOR_INIT() pwr_mgmt_cpu_usage_monitor_init()
  142. #define PWR_MGMT_CPU_USAGE_MONITOR_UPDATE() pwr_mgmt_cpu_usage_monitor_update()
  143. #define PWR_MGMT_CPU_USAGE_MONITOR_SUMMARY() NRF_LOG_INFO("Maximum CPU usage: %u%%", \
  144. m_max_cpu_usage)
  145. #define PWR_MGMT_CPU_USAGE_MONITOR_SECTION_ENTER() \
  146. { \
  147. uint32_t sleep_start = app_timer_cnt_get()
  148. #define PWR_MGMT_CPU_USAGE_MONITOR_SECTION_EXIT() \
  149. uint32_t sleep_end = app_timer_cnt_get(); \
  150. uint32_t sleep_duration; \
  151. sleep_duration = app_timer_cnt_diff_compute(sleep_end, \
  152. sleep_start); \
  153. m_ticks_sleeping += sleep_duration; \
  154. }
  155. static uint32_t m_ticks_sleeping; /**< Number of ticks spent in sleep mode (__WFE()). */
  156. static uint32_t m_ticks_last; /**< Number of ticks from the last CPU usage computation. */
  157. static uint8_t m_max_cpu_usage; /**< Maximum observed CPU usage (0 - 100%). */
  158. __STATIC_INLINE void pwr_mgmt_cpu_usage_monitor_init(void)
  159. {
  160. m_ticks_sleeping = 0;
  161. m_ticks_last = 0;
  162. m_max_cpu_usage = 0;
  163. }
  164. __STATIC_INLINE void pwr_mgmt_cpu_usage_monitor_update(void)
  165. {
  166. uint32_t delta;
  167. uint32_t ticks;
  168. uint8_t cpu_usage;
  169. ticks = app_timer_cnt_get();
  170. delta = app_timer_cnt_diff_compute(ticks, m_ticks_last);
  171. cpu_usage = 100 * (delta - m_ticks_sleeping) / delta;
  172. NRF_LOG_INFO("CPU Usage: %u%%", cpu_usage);
  173. if (m_max_cpu_usage < cpu_usage)
  174. {
  175. m_max_cpu_usage = cpu_usage;
  176. }
  177. m_ticks_last = ticks;
  178. m_ticks_sleeping = 0;
  179. }
  180. #else
  181. #define PWR_MGMT_CPU_USAGE_MONITOR_INIT()
  182. #define PWR_MGMT_CPU_USAGE_MONITOR_UPDATE()
  183. #define PWR_MGMT_CPU_USAGE_MONITOR_SUMMARY()
  184. #define PWR_MGMT_CPU_USAGE_MONITOR_SECTION_ENTER()
  185. #define PWR_MGMT_CPU_USAGE_MONITOR_SECTION_EXIT()
  186. #endif // NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED
  187. #if NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED
  188. #undef PWR_MGMT_TIMER_REQUIRED
  189. #define PWR_MGMT_TIMER_REQUIRED
  190. #define PWR_MGMT_STANDBY_TIMEOUT_INIT() pwr_mgmt_standby_timeout_clear()
  191. #define PWR_MGMT_STANDBY_TIMEOUT_CLEAR() pwr_mgmt_standby_timeout_clear()
  192. #define PWR_MGMT_STANDBY_TIMEOUT_CHECK() pwr_mgmt_standby_timeout_check()
  193. static uint16_t m_standby_counter; /**< Number of seconds from the last activity
  194. (@ref pwr_mgmt_feed). */
  195. __STATIC_INLINE void pwr_mgmt_standby_timeout_clear(void)
  196. {
  197. m_standby_counter = 0;
  198. }
  199. __STATIC_INLINE void pwr_mgmt_standby_timeout_check(void)
  200. {
  201. if (m_standby_counter < NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S)
  202. {
  203. m_standby_counter++;
  204. }
  205. else if (m_shutdown_started == false)
  206. {
  207. nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
  208. }
  209. }
  210. #else
  211. #define PWR_MGMT_STANDBY_TIMEOUT_INIT()
  212. #define PWR_MGMT_STANDBY_TIMEOUT_CLEAR()
  213. #define PWR_MGMT_STANDBY_TIMEOUT_CHECK()
  214. #endif // NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED
  215. #if NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY
  216. #undef PWR_MGMT_TIMER_REQUIRED
  217. #define PWR_MGMT_TIMER_REQUIRED
  218. #define PWR_MGMT_AUTO_SHUTDOWN_RETRY() pwr_mgmt_auto_shutdown_retry()
  219. __STATIC_INLINE void pwr_mgmt_auto_shutdown_retry(void)
  220. {
  221. if (m_shutdown_started)
  222. {
  223. // Try to continue the shutdown procedure.
  224. nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_CONTINUE);
  225. }
  226. }
  227. #else
  228. #define PWR_MGMT_AUTO_SHUTDOWN_RETRY()
  229. #endif // NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY
  230. #ifdef PWR_MGMT_SLEEP_IN_CRITICAL_SECTION_REQUIRED
  231. #define PWR_MGMT_SLEEP_INIT() pwr_mgmt_sleep_init()
  232. #define PWR_MGMT_SLEEP_LOCK_ACQUIRE() CRITICAL_REGION_ENTER()
  233. #define PWR_MGMT_SLEEP_LOCK_RELEASE() CRITICAL_REGION_EXIT()
  234. __STATIC_INLINE void pwr_mgmt_sleep_init(void)
  235. {
  236. #ifdef SOFTDEVICE_PRESENT
  237. ASSERT(current_int_priority_get() >= APP_IRQ_PRIORITY_LOW);
  238. #endif
  239. SCB->SCR |= SCB_SCR_SEVONPEND_Msk;
  240. }
  241. #else
  242. #define PWR_MGMT_SLEEP_INIT()
  243. #define PWR_MGMT_SLEEP_LOCK_ACQUIRE()
  244. #define PWR_MGMT_SLEEP_LOCK_RELEASE()
  245. #endif // PWR_MGMT_SLEEP_IN_CRITICAL_SECTION_REQUIRED
  246. #ifdef PWR_MGMT_TIMER_REQUIRED
  247. #include "app_timer.h"
  248. #define PWR_MGMT_TIMER_CREATE() pwr_mgmt_timer_create()
  249. APP_TIMER_DEF(m_pwr_mgmt_timer); /**< Timer used by this module. */
  250. /**@brief Handle events from m_pwr_mgmt_timer.
  251. */
  252. static void nrf_pwr_mgmt_timeout_handler(void * p_context)
  253. {
  254. PWR_MGMT_CPU_USAGE_MONITOR_UPDATE();
  255. PWR_MGMT_AUTO_SHUTDOWN_RETRY();
  256. PWR_MGMT_STANDBY_TIMEOUT_CHECK();
  257. }
  258. __STATIC_INLINE ret_code_t pwr_mgmt_timer_create(void)
  259. {
  260. ret_code_t ret_code = app_timer_create(&m_pwr_mgmt_timer,
  261. APP_TIMER_MODE_REPEATED,
  262. nrf_pwr_mgmt_timeout_handler);
  263. if (ret_code != NRF_SUCCESS)
  264. {
  265. return ret_code;
  266. }
  267. return app_timer_start(m_pwr_mgmt_timer, APP_TIMER_TICKS(1000), NULL);
  268. }
  269. #else
  270. #define PWR_MGMT_TIMER_CREATE() NRF_SUCCESS
  271. #endif // PWR_MGMT_TIMER_REQUIRED
  272. ret_code_t nrf_pwr_mgmt_init(void)
  273. {
  274. NRF_LOG_INFO("Init");
  275. m_shutdown_started = false;
  276. nrf_mtx_init(&m_sysoff_mtx);
  277. nrf_section_iter_init(&m_handlers_iter, &pwr_mgmt_data);
  278. PWR_MGMT_SLEEP_INIT();
  279. PWR_MGMT_DEBUG_PINS_INIT();
  280. PWR_MGMT_STANDBY_TIMEOUT_INIT();
  281. PWR_MGMT_CPU_USAGE_MONITOR_INIT();
  282. return PWR_MGMT_TIMER_CREATE();
  283. }
  284. void nrf_pwr_mgmt_run(void)
  285. {
  286. PWR_MGMT_FPU_SLEEP_PREPARE();
  287. PWR_MGMT_SLEEP_LOCK_ACQUIRE();
  288. PWR_MGMT_CPU_USAGE_MONITOR_SECTION_ENTER();
  289. PWR_MGMT_DEBUG_PIN_SET();
  290. // Wait for an event.
  291. #ifdef SOFTDEVICE_PRESENT
  292. if (nrf_sdh_is_enabled())
  293. {
  294. ret_code_t ret_code = sd_app_evt_wait();
  295. ASSERT((ret_code == NRF_SUCCESS) || (ret_code == NRF_ERROR_SOFTDEVICE_NOT_ENABLED));
  296. UNUSED_VARIABLE(ret_code);
  297. }
  298. else
  299. #endif // SOFTDEVICE_PRESENT
  300. {
  301. // Wait for an event.
  302. __WFE();
  303. // Clear the internal event register.
  304. __SEV();
  305. __WFE();
  306. }
  307. PWR_MGMT_DEBUG_PIN_CLEAR();
  308. PWR_MGMT_CPU_USAGE_MONITOR_SECTION_EXIT();
  309. PWR_MGMT_SLEEP_LOCK_RELEASE();
  310. }
  311. void nrf_pwr_mgmt_feed(void)
  312. {
  313. NRF_LOG_DEBUG("Feed");
  314. // It does not stop started shutdown process.
  315. PWR_MGMT_STANDBY_TIMEOUT_CLEAR();
  316. }
  317. /**@brief Function runs the shutdown procedure.
  318. */
  319. static void shutdown_process(void)
  320. {
  321. NRF_LOG_INFO("Shutdown started. Type %d", m_pwr_mgmt_evt);
  322. // Executing all callbacks.
  323. for (/* m_handlers_iter is initialized in nrf_pwr_mgmt_init(). Thanks to that each handler is
  324. called only once.*/;
  325. nrf_section_iter_get(&m_handlers_iter) != NULL;
  326. nrf_section_iter_next(&m_handlers_iter))
  327. {
  328. nrf_pwr_mgmt_shutdown_handler_t * p_handler =
  329. (nrf_pwr_mgmt_shutdown_handler_t *) nrf_section_iter_get(&m_handlers_iter);
  330. if ((*p_handler)(m_pwr_mgmt_evt))
  331. {
  332. NRF_LOG_INFO("SysOff handler 0x%08X => ready", (unsigned int)*p_handler);
  333. }
  334. else
  335. {
  336. // One of the modules is not ready.
  337. NRF_LOG_INFO("SysOff handler 0x%08X => blocking", (unsigned int)*p_handler);
  338. return;
  339. }
  340. }
  341. PWR_MGMT_CPU_USAGE_MONITOR_SUMMARY();
  342. NRF_LOG_INFO("Shutdown complete.");
  343. NRF_LOG_FINAL_FLUSH();
  344. if ((m_pwr_mgmt_evt == NRF_PWR_MGMT_EVT_PREPARE_RESET)
  345. || (m_pwr_mgmt_evt == NRF_PWR_MGMT_EVT_PREPARE_DFU))
  346. {
  347. NVIC_SystemReset();
  348. }
  349. else
  350. {
  351. // Enter System OFF.
  352. #ifdef SOFTDEVICE_PRESENT
  353. if (nrf_sdh_is_enabled())
  354. {
  355. ret_code_t ret_code = sd_power_system_off();
  356. ASSERT((ret_code == NRF_SUCCESS) || (ret_code == NRF_ERROR_SOFTDEVICE_NOT_ENABLED));
  357. UNUSED_VARIABLE(ret_code);
  358. #ifdef DEBUG
  359. while (true)
  360. {
  361. /* Since the CPU is kept on in an emulated System OFF mode, it is recommended
  362. * to add an infinite loop directly after entering System OFF, to prevent
  363. * the CPU from executing code that normally should not be executed. */
  364. __WFE();
  365. }
  366. #endif
  367. }
  368. #endif // SOFTDEVICE_PRESENT
  369. nrf_power_system_off();
  370. }
  371. }
  372. #if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
  373. /**@brief Handle events from app_scheduler.
  374. */
  375. static void scheduler_shutdown_handler(void * p_event_data, uint16_t event_size)
  376. {
  377. UNUSED_PARAMETER(p_event_data);
  378. UNUSED_PARAMETER(event_size);
  379. shutdown_process();
  380. }
  381. #endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
  382. void nrf_pwr_mgmt_shutdown(nrf_pwr_mgmt_shutdown_t shutdown_type)
  383. {
  384. // Check if shutdown procedure is not started.
  385. if (!nrf_mtx_trylock(&m_sysoff_mtx))
  386. {
  387. return;
  388. }
  389. if (shutdown_type != NRF_PWR_MGMT_SHUTDOWN_CONTINUE)
  390. {
  391. if (m_shutdown_started)
  392. {
  393. nrf_mtx_unlock(&m_sysoff_mtx);
  394. return;
  395. }
  396. else
  397. {
  398. m_pwr_mgmt_evt = (nrf_pwr_mgmt_evt_t)shutdown_type;
  399. m_shutdown_started = true;
  400. }
  401. }
  402. ASSERT(m_shutdown_started);
  403. NRF_LOG_INFO("Shutdown request %d", shutdown_type);
  404. #if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
  405. ret_code_t ret_code = app_sched_event_put(NULL, 0, scheduler_shutdown_handler);
  406. APP_ERROR_CHECK(ret_code);
  407. #else
  408. shutdown_process();
  409. #endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
  410. nrf_mtx_unlock(&m_sysoff_mtx);
  411. }
  412. #endif // NRF_MODULE_ENABLED(NRF_PWR_MGMT)