1/* 2 * Copyright (c) 2013-2014 Wind River Systems, Inc. 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7/** 8 * @file 9 * @brief ARM Cortex-M and Cortex-R power management 10 * 11 */ 12 13#include <toolchain.h> 14#include <linker/sections.h> 15 16_ASM_FILE_PROLOGUE 17 18GTEXT(z_arm_cpu_idle_init) 19GTEXT(arch_cpu_idle) 20GTEXT(arch_cpu_atomic_idle) 21 22#if defined(CONFIG_CPU_CORTEX_M) 23#define _SCB_SCR 0xE000ED10 24 25#define _SCB_SCR_SEVONPEND (1 << 4) 26#define _SCB_SCR_SLEEPDEEP (1 << 2) 27#define _SCB_SCR_SLEEPONEXIT (1 << 1) 28#define _SCR_INIT_BITS _SCB_SCR_SEVONPEND 29#endif 30 31/** 32 * 33 * @brief Initialization of CPU idle 34 * 35 * Only called by arch_kernel_init(). Sets SEVONPEND bit once for the system's 36 * duration. 37 * 38 * @return N/A 39 * 40 * C function prototype: 41 * 42 * void z_arm_cpu_idle_init(void); 43 */ 44 45SECTION_FUNC(TEXT, z_arm_cpu_idle_init) 46#if defined(CONFIG_CPU_CORTEX_M) 47 ldr r1, =_SCB_SCR 48 movs.n r2, #_SCR_INIT_BITS 49 str r2, [r1] 50#endif 51 bx lr 52 53SECTION_FUNC(TEXT, arch_cpu_idle) 54#ifdef CONFIG_TRACING 55 push {r0, lr} 56 bl sys_trace_idle 57#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) 58 pop {r0, r1} 59 mov lr, r1 60#else 61 pop {r0, lr} 62#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ 63#endif /* CONFIG_TRACING */ 64 65#if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) 66 /* 67 * PRIMASK is always cleared on ARMv7-M and ARMv8-M Mainline (not used 68 * for interrupt locking), and configuring BASEPRI to the lowest 69 * priority to ensure wake-up will cause interrupts to be serviced 70 * before entering low power state. 71 * 72 * Set PRIMASK before configuring BASEPRI to prevent interruption 73 * before wake-up. 74 */ 75 cpsid i 76 77 /* 78 * Set wake-up interrupt priority to the lowest and synchronise to 79 * ensure that this is visible to the WFI instruction. 80 */ 81 eors.n r0, r0 82 msr BASEPRI, r0 83 isb 84#else 85 /* 86 * For all the other ARM architectures that do not implement BASEPRI, 87 * PRIMASK is used as the interrupt locking mechanism, and it is not 88 * necessary to set PRIMASK here, as PRIMASK would have already been 89 * set by the caller as part of interrupt locking if necessary 90 * (i.e. if the caller sets _kernel.idle). 91 */ 92#endif /* CONFIG_ARMV7_M_ARMV8_M_MAINLINE */ 93 94 /* 95 * Wait for all memory transactions to complete before entering low 96 * power state. 97 */ 98 dsb 99 100 /* Enter low power state */ 101 wfi 102 103 /* 104 * Clear PRIMASK and flush instruction buffer to immediately service 105 * the wake-up interrupt. 106 */ 107 cpsie i 108 isb 109 110 bx lr 111 112SECTION_FUNC(TEXT, arch_cpu_atomic_idle) 113#ifdef CONFIG_TRACING 114 push {r0, lr} 115 bl sys_trace_idle 116#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) 117 pop {r0, r1} 118 mov lr, r1 119#else 120 pop {r0, lr} 121#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ 122#endif /* CONFIG_TRACING */ 123 124 /* 125 * Lock PRIMASK while sleeping: wfe will still get interrupted by 126 * incoming interrupts but the CPU will not service them right away. 127 */ 128 cpsid i 129 130 /* 131 * No need to set SEVONPEND, it's set once in z_arm_cpu_idle_init() 132 * and never touched again. 133 */ 134 135 /* r0: interrupt mask from caller */ 136 137#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) \ 138 || defined(CONFIG_ARMV7_R) 139 /* No BASEPRI, call wfe directly 140 * (SEVONPEND is set in z_arm_cpu_idle_init()) 141 */ 142 wfe 143 144 cmp r0, #0 145 bne _irq_disabled 146 cpsie i 147_irq_disabled: 148 149#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) 150 /* r1: zero, for setting BASEPRI (needs a register) */ 151 eors.n r1, r1 152 153 /* unlock BASEPRI so wfe gets interrupted by incoming interrupts */ 154 msr BASEPRI, r1 155 156 wfe 157 158 msr BASEPRI, r0 159 cpsie i 160#else 161#error Unknown ARM architecture 162#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ 163 bx lr 164