1#include <linux/linkage.h> 2#include <linux/threads.h> 3#include <asm/asm-offsets.h> 4#include <asm/assembler.h> 5#include <asm/glue-cache.h> 6#include <asm/glue-proc.h> 7 .text 8 9/* 10 * Save CPU state for a suspend. This saves the CPU general purpose 11 * registers, and allocates space on the kernel stack to save the CPU 12 * specific registers and some other data for resume. 13 * r0 = suspend function arg0 14 * r1 = suspend function 15 */ 16ENTRY(__cpu_suspend) 17 stmfd sp!, {r4 - r11, lr} 18#ifdef MULTI_CPU 19 ldr r10, =processor 20 ldr r4, [r10, #CPU_SLEEP_SIZE] @ size of CPU sleep state 21#else 22 ldr r4, =cpu_suspend_size 23#endif 24 mov r5, sp @ current virtual SP 25 add r4, r4, #12 @ Space for pgd, virt sp, phys resume fn 26 sub sp, sp, r4 @ allocate CPU state on stack 27 stmfd sp!, {r0, r1} @ save suspend func arg and pointer 28 add r0, sp, #8 @ save pointer to save block 29 mov r1, r4 @ size of save block 30 mov r2, r5 @ virtual SP 31 ldr r3, =sleep_save_sp 32#ifdef CONFIG_SMP 33 ALT_SMP(mrc p15, 0, lr, c0, c0, 5) 34 ALT_UP(mov lr, #0) 35 and lr, lr, #15 36 add r3, r3, lr, lsl #2 37#endif 38 bl __cpu_suspend_save 39 adr lr, BSYM(cpu_suspend_abort) 40 ldmfd sp!, {r0, pc} @ call suspend fn 41ENDPROC(__cpu_suspend) 42 .ltorg 43 44cpu_suspend_abort: 45 ldmia sp!, {r1 - r3} @ pop phys pgd, virt SP, phys resume fn 46 teq r0, #0 47 moveq r0, #1 @ force non-zero value 48 mov sp, r2 49 ldmfd sp!, {r4 - r11, pc} 50ENDPROC(cpu_suspend_abort) 51 52/* 53 * r0 = control register value 54 */ 55 .align 5 56 .pushsection .idmap.text,"ax" 57ENTRY(cpu_resume_mmu) 58 ldr r3, =cpu_resume_after_mmu 59 instr_sync 60 mcr p15, 0, r0, c1, c0, 0 @ turn on MMU, I-cache, etc 61 mrc p15, 0, r0, c0, c0, 0 @ read id reg 62 instr_sync 63 mov r0, r0 64 mov r0, r0 65 mov pc, r3 @ jump to virtual address 66ENDPROC(cpu_resume_mmu) 67 .popsection 68cpu_resume_after_mmu: 69 bl cpu_init @ restore the und/abt/irq banked regs 70 mov r0, #0 @ return zero on success 71 ldmfd sp!, {r4 - r11, pc} 72ENDPROC(cpu_resume_after_mmu) 73 74/* 75 * Note: Yes, part of the following code is located into the .data section. 76 * This is to allow sleep_save_sp to be accessed with a relative load 77 * while we can't rely on any MMU translation. We could have put 78 * sleep_save_sp in the .text section as well, but some setups might 79 * insist on it to be truly read-only. 80 */ 81 .data 82 .align 83ENTRY(cpu_resume) 84#ifdef CONFIG_SMP 85 adr r0, sleep_save_sp 86 ALT_SMP(mrc p15, 0, r1, c0, c0, 5) 87 ALT_UP(mov r1, #0) 88 and r1, r1, #15 89 ldr r0, [r0, r1, lsl #2] @ stack phys addr 90#else 91 ldr r0, sleep_save_sp @ stack phys addr 92#endif 93 setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1 @ set SVC, irqs off 94 @ load phys pgd, stack, resume fn 95 ARM( ldmia r0!, {r1, sp, pc} ) 96THUMB( ldmia r0!, {r1, r2, r3} ) 97THUMB( mov sp, r2 ) 98THUMB( bx r3 ) 99ENDPROC(cpu_resume) 100 101sleep_save_sp: 102 .rept CONFIG_NR_CPUS 103 .long 0 @ preserve stack phys ptr here 104 .endr 105