1/* 2 * Copyright (c) 2022, Carlo Caione <ccaione@baylibre.com> 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7/** 8 * @file 9 * @brief ARM Cortex-M suspend-to-RAM code (S2RAM) 10 */ 11 12#include <zephyr/toolchain.h> 13#include <offsets_short.h> 14#include <zephyr/arch/cpu.h> 15#include <zephyr/arch/common/pm_s2ram.h> 16 17/** 18 * Macro expanding to an integer literal equal to the offset of 19 * field `sr_name` in `struct __cpu_context`. This macro has to 20 * be implemented in C, because GEN_OFFSET_SYM provides offsets 21 * as C preprocessor definitions - there are not visible to the 22 * assembler. 23 * 24 * See also: `arch/arm/core/offsets/offsets_aarch32.c` 25 */ 26#define CPU_CTX_SR_OFFSET(sr_name) \ 27 ___cpu_context_t_ ## sr_name ## _OFFSET 28 29/** 30 * Macros used to save / load a special register in __cpu_context. 31 * These also have to be implemented in C due to CPU_CTX_SR_OFFSET. 32 */ 33#define SAVE_SPECIAL_REG(sr_name, cpu_ctx_reg, tmp_reg) \ 34 mrs tmp_reg, sr_name; \ 35 str tmp_reg, [cpu_ctx_reg, # CPU_CTX_SR_OFFSET(sr_name)]; 36 37#define RESTORE_SPECIAL_REG(sr_name, cpu_ctx_reg, tmp_reg) \ 38 ldr tmp_reg, [cpu_ctx_reg, # CPU_CTX_SR_OFFSET(sr_name)]; \ 39 msr sr_name, tmp_reg; 40 41/* 42 * The following macros could be written as assembler macros, but C is used 43 * for portability (assembler macro syntax may differ between toolchains). 44 */ 45 46/* 47 * Pushes registers r4~r12 and lr on the stack. 48 * r0 is unmodified but other GPRs may be overwritten. 49 */ 50#if !defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) 51/* `push` on ARMv6-M / ARMv8-M Baseline: 52 * only r0~r7 and lr may be pushed 53 */ 54#define PUSH_GPRS \ 55 push {r4-r7}; \ 56 mov r1, r8; \ 57 mov r2, r9; \ 58 mov r3, r10; \ 59 mov r4, r11; \ 60 mov r5, r12; \ 61 push {r1-r5, lr} 62#else 63/* `push` on ARMv7-M and ARMv8-M Mainline: no limitation */ 64#define PUSH_GPRS \ 65 push {r4-r12, lr} 66#endif /* !CONFIG_ARMV7_M_ARMV8_M_MAINLINE */ 67 68/* 69 * Pops registers r4~r12 and lr from the stack 70 * r0 is unmodified but other GPRs may be overwritten. 71 */ 72#if !defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) 73/* `pop` on ARMv6-M / ARMv8-M Baseline: 74 * can only pop to r0~r7 and pc (not lr!) 75 */ 76#define POP_GPRS \ 77 pop {r1-r6}; \ 78 mov lr, r6; \ 79 mov r12, r5; \ 80 mov r11, r4; \ 81 mov r10, r3; \ 82 mov r9, r2; \ 83 mov r8, r1; \ 84 pop {r4-r7} 85#else 86/* `pop` on ARMv7-M and ARMv8-M Mainline: no limitation */ 87#define POP_GPRS \ 88 pop {r4-r12, lr} 89#endif /* !CONFIG_ARMV7_M_ARMV8_M_MAINLINE */ 90 91 92#if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) 93/* Registers present only on ARMv7-M and ARMv8-M Mainline */ 94#define SAVE_FM_BP_REGS(cpu_ctx, tmp_reg) \ 95 SAVE_SPECIAL_REG(faultmask, cpu_ctx, tmp_reg) \ 96 SAVE_SPECIAL_REG(basepri, cpu_ctx, tmp_reg) 97 98#define RESTORE_FM_BP_REGS(cpu_ctx, tmp_reg) \ 99 RESTORE_SPECIAL_REG(faultmask, cpu_ctx, tmp_reg) \ 100 RESTORE_SPECIAL_REG(basepri, cpu_ctx, tmp_reg) 101#else 102/* Registers not present: do nothing */ 103#define SAVE_FM_BP_REGS(cpu_ctx, tmp_reg) 104#define RESTORE_FM_BP_REGS(cpu_ctx, tmp_reg) 105#endif /* CONFIG_ARMV7_M_ARMV8_M_MAINLINE */ 106 107#if defined(CONFIG_CPU_CORTEX_M_HAS_SPLIM) 108/* Registers present only on certain ARMv8-M implementations */ 109#define SAVE_SPLIM_REGS(cpu_ctx, tmp_reg) \ 110 SAVE_SPECIAL_REG(msplim, cpu_ctx, tmp_reg) \ 111 SAVE_SPECIAL_REG(psplim, cpu_ctx, tmp_reg) 112 113#define RESTORE_SPLIM_REGS(cpu_ctx, tmp_reg) \ 114 RESTORE_SPECIAL_REG(msplim, cpu_ctx, tmp_reg) \ 115 RESTORE_SPECIAL_REG(psplim, cpu_ctx, tmp_reg) 116#else 117/* Registers not present: do nothing */ 118#define SAVE_SPLIM_REGS(cpu_ctx, tmp_reg) 119#define RESTORE_SPLIM_REGS(cpu_ctx, tmp_reg) 120#endif /* CONFIG_CPU_CORTEX_M_HAS_SPLIM */ 121 122/* 123 * Saves the CPU's special registers in the `struct __cpu_context` 124 * pointed to by the `cpu_ctx` register. 125 * The `tmp_reg` register is overwritten as part of this process. 126 */ 127#define SAVE_SPECIAL_REGISTERS(cpu_ctx, tmp_reg) \ 128 SAVE_SPECIAL_REG(msp, cpu_ctx, tmp_reg) \ 129 SAVE_SPECIAL_REG(psp, cpu_ctx, tmp_reg) \ 130 SAVE_SPECIAL_REG(primask, cpu_ctx, tmp_reg) \ 131 SAVE_SPLIM_REGS( cpu_ctx, tmp_reg) \ 132 SAVE_FM_BP_REGS( cpu_ctx, tmp_reg) \ 133 SAVE_SPECIAL_REG(control, cpu_ctx, tmp_reg) 134 135/* 136 * Restores the CPU's special registers from the `struct __cpu_context` 137 * pointed to by the `cpu_ctx` register. 138 * The `tmp_reg` register is overwritten as part of this process. 139 * 140 * N.B.: ISB at the end is required because "Software must use an ISB 141 * barrier instruction to ensure a write to the CONTROL register takes 142 * effect before the next instruction is executed." 143 * 144 * If this macro is modified, make sure CONTROL is always the last 145 * restored register, and that an ISB follows the MSR instruction. 146 */ 147#define RESTORE_SPECIAL_REGISTERS(cpu_ctx, tmp_reg) \ 148 RESTORE_SPECIAL_REG(msp, cpu_ctx, tmp_reg) \ 149 RESTORE_SPECIAL_REG(psp, cpu_ctx, tmp_reg) \ 150 RESTORE_SPECIAL_REG(primask, cpu_ctx, tmp_reg) \ 151 RESTORE_SPLIM_REGS( cpu_ctx, tmp_reg) \ 152 RESTORE_FM_BP_REGS( cpu_ctx, tmp_reg) \ 153 RESTORE_SPECIAL_REG(control, cpu_ctx, tmp_reg) \ 154 isb 155 156_ASM_FILE_PROLOGUE 157 158GTEXT(pm_s2ram_mark_set) 159GTEXT(pm_s2ram_mark_check_and_clear) 160GDATA(_cpu_context) 161 162SECTION_FUNC(TEXT, arch_pm_s2ram_suspend) 163 /* 164 * Save the CPU context 165 * 166 * r0: address of the system_off function 167 */ 168 PUSH_GPRS 169 170 /* Move system_off to protected register. */ 171 mov r4, r0 172 173 /* Store CPU context */ 174 ldr r1, =_cpu_context 175 176 SAVE_SPECIAL_REGISTERS(/* ctx: */ r1, /* tmp: */ r2) 177 178 /* 179 * Mark entering suspend to RAM. 180 */ 181 mov r1, lr 182 bl pm_s2ram_mark_set 183 mov lr, r1 184 185 /* 186 * Call the system_off function passed as parameter. This should never 187 * return. 188 */ 189 blx r4 190 191 /* 192 * The system_off function returns here only when the powering off was 193 * not successful (in r0 the return value). 194 */ 195 196 /* Move return value of system_off to callee-saved register. */ 197 mov r4, r0 198 199 /* 200 * Reset the marking of suspend to RAM, return is ignored. 201 */ 202 mov r1, lr 203 bl pm_s2ram_mark_check_and_clear 204 mov lr, r1 205 206 /* Move the stored return value of system_off back to r0, 207 * setting it as return value for this function. 208 */ 209 mov r0, r4 210 211 POP_GPRS 212 bx lr 213 214 215GTEXT(arch_pm_s2ram_resume) 216SECTION_FUNC(TEXT, arch_pm_s2ram_resume) 217 /* 218 * Check if reset occurred after suspending to RAM. 219 */ 220 mov r1, lr 221 bl pm_s2ram_mark_check_and_clear 222 mov lr, r1 223 cmp r0, #0x1 224 beq resume 225 bx lr 226 227resume: 228 /* 229 * Restore the CPU context 230 */ 231 ldr r0, =_cpu_context 232 233 RESTORE_SPECIAL_REGISTERS(/* ctx: */ r0, /* tmp: */ r1) 234 235 POP_GPRS 236 237 /* 238 * Set the return value and return 239 */ 240 movs r0, #0 241 bx lr 242