1 /* 2 * Copyright (c) 2013-2014 Wind River Systems, Inc. 3 * Copyright (c) 2023 Arm Limited 4 * 5 * SPDX-License-Identifier: Apache-2.0 6 */ 7 8 /** 9 * @file 10 * @brief ARM Cortex-M power management 11 */ 12 #include <zephyr/kernel.h> 13 #include <cmsis_core.h> 14 15 #if defined(CONFIG_ARM_ON_EXIT_CPU_IDLE) 16 #include <soc_cpu_idle.h> 17 #endif 18 19 /** 20 * @brief Initialization of CPU idle 21 * 22 * Only called by arch_kernel_init(). Sets SEVONPEND bit once for the system's 23 * duration. 24 */ z_arm_cpu_idle_init(void)25void z_arm_cpu_idle_init(void) 26 { 27 SCB->SCR = SCB_SCR_SEVONPEND_Msk; 28 } 29 30 #if defined(CONFIG_ARM_ON_EXIT_CPU_IDLE) 31 #define ON_EXIT_IDLE_HOOK SOC_ON_EXIT_CPU_IDLE 32 #else 33 #define ON_EXIT_IDLE_HOOK do {} while (false) 34 #endif 35 36 #if defined(CONFIG_ARM_ON_ENTER_CPU_IDLE_HOOK) 37 #define SLEEP_IF_ALLOWED(wait_instr) do { \ 38 /* Skip the wait instr if on_enter_cpu_idle returns false */ \ 39 if (z_arm_on_enter_cpu_idle()) { \ 40 /* Wait for all memory transaction to complete */ \ 41 /* before entering low power state. */ \ 42 __DSB(); \ 43 wait_instr(); \ 44 /* Inline the macro provided by SoC-specific code */ \ 45 ON_EXIT_IDLE_HOOK; \ 46 } \ 47 } while (false) 48 #else 49 #define SLEEP_IF_ALLOWED(wait_instr) do { \ 50 __DSB(); \ 51 wait_instr(); \ 52 ON_EXIT_IDLE_HOOK; \ 53 } while (false) 54 #endif 55 56 #ifndef CONFIG_ARCH_HAS_CUSTOM_CPU_IDLE arch_cpu_idle(void)57void arch_cpu_idle(void) 58 { 59 #if defined(CONFIG_TRACING) 60 sys_trace_idle(); 61 #endif 62 63 #if CONFIG_ARM_ON_ENTER_CPU_IDLE_PREPARE_HOOK 64 z_arm_on_enter_cpu_idle_prepare(); 65 #endif 66 67 #if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) 68 /* 69 * PRIMASK is always cleared on ARMv7-M and ARMv8-M (not used 70 * for interrupt locking), and configuring BASEPRI to the lowest 71 * priority to ensure wake-up will cause interrupts to be serviced 72 * before entering low power state. 73 * 74 * Set PRIMASK before configuring BASEPRI to prevent interruption 75 * before wake-up. 76 */ 77 __disable_irq(); 78 79 /* 80 * Set wake-up interrupt priority to the lowest and synchronize to 81 * ensure that this is visible to the WFI instruction. 82 */ 83 __set_BASEPRI(0); 84 __ISB(); 85 #else 86 /* 87 * For all the other ARM architectures that do not implement BASEPRI, 88 * PRIMASK is used as the interrupt locking mechanism, and it is not 89 * necessary to set PRIMASK here, as PRIMASK would have already been 90 * set by the caller as part of interrupt locking if necessary 91 * (i.e. if the caller sets _kernel.idle). 92 */ 93 #endif 94 95 SLEEP_IF_ALLOWED(__WFI); 96 97 __enable_irq(); 98 __ISB(); 99 } 100 #endif 101 102 #ifndef CONFIG_ARCH_HAS_CUSTOM_CPU_ATOMIC_IDLE arch_cpu_atomic_idle(unsigned int key)103void arch_cpu_atomic_idle(unsigned int key) 104 { 105 #if defined(CONFIG_TRACING) 106 sys_trace_idle(); 107 #endif 108 109 #if CONFIG_ARM_ON_ENTER_CPU_IDLE_PREPARE_HOOK 110 z_arm_on_enter_cpu_idle_prepare(); 111 #endif 112 113 /* 114 * Lock PRIMASK while sleeping: wfe will still get interrupted by 115 * incoming interrupts but the CPU will not service them right away. 116 */ 117 __disable_irq(); 118 119 /* 120 * No need to set SEVONPEND, it's set once in z_arm_cpu_idle_init() 121 * and never touched again. 122 */ 123 124 #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) 125 /* No BASEPRI, call wfe directly. (SEVONPEND is set in z_arm_cpu_idle_init()) */ 126 #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) 127 /* unlock BASEPRI so wfe gets interrupted by incoming interrupts */ 128 __set_BASEPRI(0); 129 __ISB(); 130 #else 131 #error Unsupported architecture 132 #endif 133 134 SLEEP_IF_ALLOWED(__WFE); 135 136 arch_irq_unlock(key); 137 #if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) 138 __enable_irq(); 139 #endif 140 } 141 #endif 142