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